]> git.ipfire.org Git - thirdparty/git.git/blobdiff - diff.c
Merge branch 'en/remerge-diff'
[thirdparty/git.git] / diff.c
diff --git a/diff.c b/diff.c
index c862771a58939f0cd2a20b2056c8d93b17c7be26..32f0c0123609cbf3657bab5baeff4dc146399aa0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -28,6 +28,7 @@
 #include "help.h"
 #include "promisor-remote.h"
 #include "dir.h"
+#include "strmap.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -3353,6 +3354,31 @@ struct userdiff_driver *get_textconv(struct repository *r,
        return userdiff_get_textconv(r, one->driver);
 }
 
+static struct strbuf *additional_headers(struct diff_options *o,
+                                        const char *path)
+{
+       if (!o->additional_path_headers)
+               return NULL;
+       return strmap_get(o->additional_path_headers, path);
+}
+
+static void add_formatted_headers(struct strbuf *msg,
+                                 struct strbuf *more_headers,
+                                 const char *line_prefix,
+                                 const char *meta,
+                                 const char *reset)
+{
+       char *next, *newline;
+
+       for (next = more_headers->buf; *next; next = newline) {
+               newline = strchrnul(next, '\n');
+               strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta,
+                           (int)(newline - next), next, reset);
+               if (*newline)
+                       newline++;
+       }
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -3411,6 +3437,17 @@ static void builtin_diff(const char *name_a,
        b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
+       if (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two)) {
+               /*
+                * We should only reach this point for pairs from
+                * create_filepairs_for_header_only_notifications().  For
+                * these, we should avoid the "/dev/null" special casing
+                * above, meaning we avoid showing such pairs as either
+                * "new file" or "deleted file" below.
+                */
+               lbl[0] = a_one;
+               lbl[1] = b_two;
+       }
        strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
@@ -4275,6 +4312,7 @@ static void fill_metainfo(struct strbuf *msg,
        const char *set = diff_get_color(use_color, DIFF_METAINFO);
        const char *reset = diff_get_color(use_color, DIFF_RESET);
        const char *line_prefix = diff_line_prefix(o);
+       struct strbuf *more_headers = NULL;
 
        *must_show_header = 1;
        strbuf_init(msg, PATH_MAX * 2 + 300);
@@ -4311,6 +4349,11 @@ static void fill_metainfo(struct strbuf *msg,
        default:
                *must_show_header = 0;
        }
+       if ((more_headers = additional_headers(o, name))) {
+               add_formatted_headers(msg, more_headers,
+                                     line_prefix, set, reset);
+               *must_show_header = 1;
+       }
        if (one && two && !oideq(&one->oid, &two->oid)) {
                const unsigned hexsz = the_hash_algo->hexsz;
                int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV;
@@ -5803,12 +5846,27 @@ int diff_unmodified_pair(struct diff_filepair *p)
 
 static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
 {
-       if (diff_unmodified_pair(p))
+       int include_conflict_headers =
+           (additional_headers(o, p->one->path) &&
+            (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
+
+       /*
+        * Check if we can return early without showing a diff.  Note that
+        * diff_filepair only stores {oid, path, mode, is_valid}
+        * information for each path, and thus diff_unmodified_pair() only
+        * considers those bits of info.  However, we do not want pairs
+        * created by create_filepairs_for_header_only_notifications()
+        * (which always look like unmodified pairs) to be ignored, so
+        * return early if both p is unmodified AND we don't want to
+        * include_conflict_headers.
+        */
+       if (diff_unmodified_pair(p) && !include_conflict_headers)
                return;
 
+       /* Actually, we can also return early to avoid showing tree diffs */
        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-               return; /* no tree diffs in patch format */
+               return;
 
        run_diff(p, o);
 }
@@ -5839,10 +5897,17 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
        run_checkdiff(p, o);
 }
 
-int diff_queue_is_empty(void)
+int diff_queue_is_empty(struct diff_options *o)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
+       int include_conflict_headers =
+           (o->additional_path_headers &&
+            (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
+
+       if (include_conflict_headers)
+               return 0;
+
        for (i = 0; i < q->nr; i++)
                if (!diff_unmodified_pair(q->queue[i]))
                        return 0;
@@ -6276,6 +6341,54 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
                warning(_(rename_limit_advice), varname, needed);
 }
 
+static void create_filepairs_for_header_only_notifications(struct diff_options *o)
+{
+       struct strset present;
+       struct diff_queue_struct *q = &diff_queued_diff;
+       struct hashmap_iter iter;
+       struct strmap_entry *e;
+       int i;
+
+       strset_init_with_options(&present, /*pool*/ NULL, /*strdup*/ 0);
+
+       /*
+        * Find out which paths exist in diff_queued_diff, preferring
+        * one->path for any pair that has multiple paths.
+        */
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               char *path = p->one->path ? p->one->path : p->two->path;
+
+               if (strmap_contains(o->additional_path_headers, path))
+                       strset_add(&present, path);
+       }
+
+       /*
+        * Loop over paths in additional_path_headers; for each NOT already
+        * in diff_queued_diff, create a synthetic filepair and insert that
+        * into diff_queued_diff.
+        */
+       strmap_for_each_entry(o->additional_path_headers, &iter, e) {
+               if (!strset_contains(&present, e->key)) {
+                       struct diff_filespec *one, *two;
+                       struct diff_filepair *p;
+
+                       one = alloc_filespec(e->key);
+                       two = alloc_filespec(e->key);
+                       fill_filespec(one, null_oid(), 0, 0);
+                       fill_filespec(two, null_oid(), 0, 0);
+                       p = diff_queue(q, one, two);
+                       p->status = DIFF_STATUS_MODIFIED;
+               }
+       }
+
+       /* Re-sort the filepairs */
+       diffcore_fix_diff_index();
+
+       /* Cleanup */
+       strset_clear(&present);
+}
+
 static void diff_flush_patch_all_file_pairs(struct diff_options *o)
 {
        int i;
@@ -6288,6 +6401,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
        if (o->color_moved)
                o->emitted_symbols = &esm;
 
+       if (o->additional_path_headers)
+               create_filepairs_for_header_only_notifications(o);
+
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                if (check_pair_status(p))
@@ -6358,7 +6474,7 @@ void diff_flush(struct diff_options *options)
         * Order: raw, stat, summary, patch
         * or:    name/name-status/checkdiff (other bits clear)
         */
-       if (!q->nr)
+       if (!q->nr && !options->additional_path_headers)
                goto free_queue;
 
        if (output_format & (DIFF_FORMAT_RAW |