]> git.ipfire.org Git - thirdparty/git.git/commitdiff
show, log: include conflict/warning messages in --remerge-diff headers
authorElijah Newren <newren@gmail.com>
Wed, 2 Feb 2022 02:37:35 +0000 (02:37 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 2 Feb 2022 18:02:28 +0000 (10:02 -0800)
Conflicts such as modify/delete, rename/rename, or file/directory are
not representable via content conflict markers, and the normal output
messages notifying users about these were dropped with --remerge-diff.
While we don't want these messages randomly shown before the commit
and diff headers, we do want them to still be shown; include them as
part of the diff headers instead.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
log-tree.c
merge-ort.c
merge-ort.h
t/t4069-remerge-diff.sh

index fe2084625ec16869b3398dcc7d287aebc3cc86bc..25165e2a915e0394cbfc2347d5f3eca83842beff 100644 (file)
@@ -19,6 +19,7 @@
 #include "line-log.h"
 #include "help.h"
 #include "range-diff.h"
+#include "strmap.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -907,6 +908,52 @@ static int do_diff_combined(struct rev_info *opt, struct commit *commit)
        return !opt->loginfo;
 }
 
+static void setup_additional_headers(struct diff_options *o,
+                                    struct strmap *all_headers)
+{
+       struct hashmap_iter iter;
+       struct strmap_entry *entry;
+
+       /*
+        * Make o->additional_path_headers contain the subset of all_headers
+        * that match o->pathspec.  If there aren't any that match o->pathspec,
+        * then make o->additional_path_headers be NULL.
+        */
+
+       if (!o->pathspec.nr) {
+               o->additional_path_headers = all_headers;
+               return;
+       }
+
+       o->additional_path_headers = xmalloc(sizeof(struct strmap));
+       strmap_init_with_options(o->additional_path_headers, NULL, 0);
+       strmap_for_each_entry(all_headers, &iter, entry) {
+               if (match_pathspec(the_repository->index, &o->pathspec,
+                                  entry->key, strlen(entry->key),
+                                  0 /* prefix */, NULL /* seen */,
+                                  0 /* is_dir */))
+                       strmap_put(o->additional_path_headers,
+                                  entry->key, entry->value);
+       }
+       if (!strmap_get_size(o->additional_path_headers)) {
+               strmap_clear(o->additional_path_headers, 0);
+               FREE_AND_NULL(o->additional_path_headers);
+       }
+}
+
+static void cleanup_additional_headers(struct diff_options *o)
+{
+       if (!o->pathspec.nr) {
+               o->additional_path_headers = NULL;
+               return;
+       }
+       if (!o->additional_path_headers)
+               return;
+
+       strmap_clear(o->additional_path_headers, 0);
+       FREE_AND_NULL(o->additional_path_headers);
+}
+
 static int do_remerge_diff(struct rev_info *opt,
                           struct commit_list *parents,
                           struct object_id *oid,
@@ -924,6 +971,8 @@ static int do_remerge_diff(struct rev_info *opt,
        /* Setup merge options */
        init_merge_options(&o, the_repository);
        o.show_rename_progress = 0;
+       o.record_conflict_msgs_as_headers = 1;
+       o.msg_header_prefix = "remerge";
 
        ctx.abbrev = DEFAULT_ABBREV;
        format_commit_message(parent1, "%h (%s)", &parent1_desc, &ctx);
@@ -940,10 +989,12 @@ static int do_remerge_diff(struct rev_info *opt,
        merge_incore_recursive(&o, bases, parent1, parent2, &res);
 
        /* Show the diff */
+       setup_additional_headers(&opt->diffopt, res.path_messages);
        diff_tree_oid(&res.tree->object.oid, oid, "", &opt->diffopt);
        log_tree_diff_flush(opt);
 
        /* Cleanup */
+       cleanup_additional_headers(&opt->diffopt);
        strbuf_release(&parent1_desc);
        strbuf_release(&parent2_desc);
        merge_finalize(&o, &res);
index 481305d2bcf0a21a52f0a051b3a93fa1a7a05be0..43f980d2586364d9828d2d532fa153b1b5eadca3 100644 (file)
@@ -4585,6 +4585,7 @@ redo:
        trace2_region_leave("merge", "process_entries", opt->repo);
 
        /* Set return values */
+       result->path_messages = &opt->priv->output;
        result->tree = parse_tree_indirect(&working_tree_oid);
        /* existence of conflicted entries implies unclean */
        result->clean &= strmap_empty(&opt->priv->conflicted);
index c011864ffeb115b43a26940a8f0b24d33c8e8182..fe599b8786891c74cbc5e911689f4a4d319152cd 100644 (file)
@@ -5,6 +5,7 @@
 
 struct commit;
 struct tree;
+struct strmap;
 
 struct merge_result {
        /*
@@ -23,6 +24,15 @@ struct merge_result {
         */
        struct tree *tree;
 
+       /*
+        * Special messages and conflict notices for various paths
+        *
+        * This is a map of pathnames to strbufs.  It contains various
+        * warning/conflict/notice messages (possibly multiple per path)
+        * that callers may want to use.
+        */
+       struct strmap *path_messages;
+
        /*
         * Additional metadata used by merge_switch_to_result() or future calls
         * to merge_incore_*().  Includes data needed to update the index (if
index d7ab0f50066e490a8274c69235b23a3da3b44d11..fd6bce6478121452c8bea00f2cae0f080982052b 100755 (executable)
@@ -60,6 +60,7 @@ test_expect_success 'remerge-diff with both a resolved conflict and an unrelated
        git log -1 --oneline ab_resolution >tmp &&
        cat <<-EOF >>tmp &&
        diff --git a/numbers b/numbers
+       remerge CONFLICT (content): Merge conflict in numbers
        index a1fb731..6875544 100644
        --- a/numbers
        +++ b/numbers
@@ -88,4 +89,147 @@ test_expect_success 'remerge-diff with both a resolved conflict and an unrelated
        test_cmp expect actual
 '
 
+test_expect_success 'setup non-content conflicts' '
+       git switch --orphan base &&
+
+       test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
+       test_write_lines a b c d e f g h i >letters &&
+       test_write_lines in the way >content &&
+       git add numbers letters content &&
+       git commit -m base &&
+
+       git branch side1 &&
+       git branch side2 &&
+
+       git checkout side1 &&
+       test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
+       git mv letters letters_side1 &&
+       git mv content file_or_directory &&
+       git add numbers &&
+       git commit -m side1 &&
+
+       git checkout side2 &&
+       git rm numbers &&
+       git mv letters letters_side2 &&
+       mkdir file_or_directory &&
+       echo hello >file_or_directory/world &&
+       git add file_or_directory/world &&
+       git commit -m side2 &&
+
+       git checkout -b resolution side1 &&
+       test_must_fail git merge side2 &&
+       test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
+       git add numbers &&
+       git add letters_side1 &&
+       git rm letters &&
+       git rm letters_side2 &&
+       git add file_or_directory~HEAD &&
+       git mv file_or_directory~HEAD wanted_content &&
+       git commit -m resolved
+'
+
+test_expect_success 'remerge-diff with non-content conflicts' '
+       git log -1 --oneline resolution >tmp &&
+       cat <<-EOF >>tmp &&
+       diff --git a/file_or_directory~HASH (side1) b/wanted_content
+       similarity index 100%
+       rename from file_or_directory~HASH (side1)
+       rename to wanted_content
+       remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+       diff --git a/letters b/letters
+       remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+       diff --git a/letters_side2 b/letters_side2
+       deleted file mode 100644
+       index b236ae5..0000000
+       --- a/letters_side2
+       +++ /dev/null
+       @@ -1,9 +0,0 @@
+       -a
+       -b
+       -c
+       -d
+       -e
+       -f
+       -g
+       -h
+       -i
+       diff --git a/numbers b/numbers
+       remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1).  Version HASH (side1) of numbers left in tree.
+       EOF
+       # We still have some sha1 hashes above; rip them out so test works
+       # with sha256
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+       git show --oneline --remerge-diff resolution >tmp &&
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ diff-filter=U: all conflict headers, no diff content' '
+       git log -1 --oneline resolution >tmp &&
+       cat <<-EOF >>tmp &&
+       diff --git a/file_or_directory~HASH (side1) b/file_or_directory~HASH (side1)
+       remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+       diff --git a/letters b/letters
+       remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+       diff --git a/numbers b/numbers
+       remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1).  Version HASH (side1) of numbers left in tree.
+       EOF
+       # We still have some sha1 hashes above; rip them out so test works
+       # with sha256
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+       git show --oneline --remerge-diff --diff-filter=U resolution >tmp &&
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ diff-filter=R: relevant file + conflict header' '
+       git log -1 --oneline resolution >tmp &&
+       cat <<-EOF >>tmp &&
+       diff --git a/file_or_directory~HASH (side1) b/wanted_content
+       similarity index 100%
+       rename from file_or_directory~HASH (side1)
+       rename to wanted_content
+       remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+       EOF
+       # We still have some sha1 hashes above; rip them out so test works
+       # with sha256
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+       git show --oneline --remerge-diff --diff-filter=R resolution >tmp &&
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ pathspec: limits to relevant file including conflict header' '
+       git log -1 --oneline resolution >tmp &&
+       cat <<-EOF >>tmp &&
+       diff --git a/letters b/letters
+       remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+       diff --git a/letters_side2 b/letters_side2
+       deleted file mode 100644
+       index b236ae5..0000000
+       --- a/letters_side2
+       +++ /dev/null
+       @@ -1,9 +0,0 @@
+       -a
+       -b
+       -c
+       -d
+       -e
+       -f
+       -g
+       -h
+       -i
+       EOF
+       # We still have some sha1 hashes above; rip them out so test works
+       # with sha256
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+       git show --oneline --remerge-diff --full-history resolution -- "letters*" >tmp &&
+       sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+       test_cmp expect actual
+'
+
 test_done