@matheusbsbs / matheus-bagliano / commits / 6abb3655ef
Fork 0

git notes merge: Manual conflict resolution, part 2/2

When the notes merge conflicts in .git/NOTES_MERGE_WORKTREE have been resolved, we need to record a new notes commit on the appropriate notes ref with the resolved notes. This patch implements 'git notes merge --commit' which the user should run after resolving conflicts in the notes merge worktree. This command finalizes the notes merge by recombining the partial notes tree from part 1 with the now-resolved conflicts in the notes merge worktree in a merge commit, and updating the appropriate ref to this merge commit. In order to correctly finalize the merge, we need to keep track of three things: - The partial merge result from part 1, containing the auto-merged notes. This is now stored into a ref called .git/NOTES_MERGE_PARTIAL. - The unmerged notes. These are already stored in .git/NOTES_MERGE_WORKTREE, thanks to part 1. - The notes ref to be updated by the finalized merge result. This is now stored in a symref called .git/NOTES_MERGE_REF. In addition to "git notes merge --commit", which uses the above details to create the finalized notes merge commit, this patch also implements "git notes merge --reset", which aborts the ongoing notes merge by simply removing the files/directory described above. FTR, "git notes merge --commit" reuses "git notes merge --reset" to remove the information described above (.git/NOTES_MERGE_*) after the notes merge have been successfully finalized. The patch also contains documentation and testcases for the two new options. This patch has been improved by the following contributions: - Ævar Arnfjörð Bjarmason: Fix nonsense sentence in --commit description - Sverre Rabbelier: Rename --reset to --abort Thanks-to: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Thanks-to: Sverre Rabbelier <srabbelier@gmail.com> Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johan Herland committed Nov 9, 2010 at 22:49 UTC 6abb3655efe364cf0375b5cdae2729af7562ed45
5 files changed +394 -4
Documentation/git-notes.txt
+22
@@ -15,6 +15,8 @@ SYNOPSIS
15 'git notes' edit [<object>]
16 'git notes' show [<object>]
17 'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
18 +'git notes' merge --commit [-v | -q]
19 +'git notes' merge --abort [-v | -q]
20 'git notes' remove [<object>]
21 'git notes' prune [-n | -v]
22
@@ -95,6 +97,9 @@ conflicting notes (see the -s/--strategy option) is not given,
97 the "manual" resolver is used. This resolver checks out the
98 conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
99 and instructs the user to manually resolve the conflicts there.
100 +When done, the user can either finalize the merge with
101 +'git notes merge --commit', or abort the merge with
102 +'git notes merge --abort'.
103
104 remove::
105 Remove the notes for a given object (defaults to HEAD).
@@ -154,6 +159,20 @@ OPTIONS
159 See the "NOTES MERGE STRATEGIES" section below for more
160 information on each notes merge strategy.
161
162 +--commit::
163 + Finalize an in-progress 'git notes merge'. Use this option
164 + when you have resolved the conflicts that 'git notes merge'
165 + stored in .git/NOTES_MERGE_WORKTREE. This amends the partial
166 + merge commit created by 'git notes merge' (stored in
167 + .git/NOTES_MERGE_PARTIAL) by adding the notes in
168 + .git/NOTES_MERGE_WORKTREE. The notes ref stored in the
169 + .git/NOTES_MERGE_REF symref is updated to the resulting commit.
170 +
171 +--abort::
172 + Abort/reset a in-progress 'git notes merge', i.e. a notes merge
173 + with conflicts. This simply removes all files related to the
174 + notes merge.
175 +
176 -q::
177 --quiet::
178 When merging notes, operate quietly.
@@ -197,6 +216,9 @@ The default notes merge strategy is "manual", which checks out
216 conflicting notes in a special work tree for resolving notes conflicts
217 (`.git/NOTES_MERGE_WORKTREE`), and instructs the user to resolve the
218 conflicts in that work tree.
219 +When done, the user can either finalize the merge with
220 +'git notes merge --commit', or abort the merge with
221 +'git notes merge --abort'.
222
223 "ours" automatically resolves conflicting notes in favor of the local
224 version (i.e. the current notes ref).
builtin/notes.c
+103 -3
@@ -27,6 +27,8 @@ static const char * const git_notes_usage[] = {
27 "git notes [--ref <notes_ref>] edit [<object>]",
28 "git notes [--ref <notes_ref>] show [<object>]",
29 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
30 + "git notes merge --commit [-v | -q]",
31 + "git notes merge --abort [-v | -q]",
32 "git notes [--ref <notes_ref>] remove [<object>]",
33 "git notes [--ref <notes_ref>] prune [-n | -v]",
34 NULL
@@ -65,6 +67,8 @@ static const char * const git_notes_show_usage[] = {
67
68 static const char * const git_notes_merge_usage[] = {
69 "git notes merge [<options>] <notes_ref>",
70 + "git notes merge --commit [<options>]",
71 + "git notes merge --abort [<options>]",
72 NULL
73 };
74
@@ -761,33 +765,119 @@ static int show(int argc, const char **argv, const char *prefix)
765 return retval;
766 }
767
768 +static int merge_abort(struct notes_merge_options *o)
769 +{
770 + int ret = 0;
771 +
772 + /*
773 + * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
774 + * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
775 + */
776 +
777 + if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
778 + ret += error("Failed to delete ref NOTES_MERGE_PARTIAL");
779 + if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
780 + ret += error("Failed to delete ref NOTES_MERGE_REF");
781 + if (notes_merge_abort(o))
782 + ret += error("Failed to remove 'git notes merge' worktree");
783 + return ret;
784 +}
785 +
786 +static int merge_commit(struct notes_merge_options *o)
787 +{
788 + struct strbuf msg = STRBUF_INIT;
789 + unsigned char sha1[20];
790 + struct notes_tree *t;
791 + struct commit *partial;
792 + struct pretty_print_context pretty_ctx;
793 +
794 + /*
795 + * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
796 + * and target notes ref from .git/NOTES_MERGE_REF.
797 + */
798 +
799 + if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
800 + die("Failed to read ref NOTES_MERGE_PARTIAL");
801 + else if (!(partial = lookup_commit_reference(sha1)))
802 + die("Could not find commit from NOTES_MERGE_PARTIAL.");
803 + else if (parse_commit(partial))
804 + die("Could not parse commit from NOTES_MERGE_PARTIAL.");
805 +
806 + t = xcalloc(1, sizeof(struct notes_tree));
807 + init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
808 +
809 + o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0);
810 + if (!o->local_ref)
811 + die("Failed to resolve NOTES_MERGE_REF");
812 +
813 + if (notes_merge_commit(o, t, partial, sha1))
814 + die("Failed to finalize notes merge");
815 +
816 + /* Reuse existing commit message in reflog message */
817 + memset(&pretty_ctx, 0, sizeof(pretty_ctx));
818 + format_commit_message(partial, "%s", &msg, &pretty_ctx);
819 + strbuf_trim(&msg);
820 + strbuf_insert(&msg, 0, "notes: ", 7);
821 + update_ref(msg.buf, o->local_ref, sha1, NULL, 0, DIE_ON_ERR);
822 +
823 + free_notes(t);
824 + strbuf_release(&msg);
825 + return merge_abort(o);
826 +}
827 +
828 static int merge(int argc, const char **argv, const char *prefix)
829 {
830 struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
831 unsigned char result_sha1[20];
832 struct notes_tree *t;
833 struct notes_merge_options o;
834 + int do_merge = 0, do_commit = 0, do_abort = 0;
835 int verbosity = 0, result;
836 const char *strategy = NULL;
837 struct option options[] = {
838 + OPT_GROUP("General options"),
839 OPT__VERBOSITY(&verbosity),
840 + OPT_GROUP("Merge options"),
841 OPT_STRING('s', "strategy", &strategy, "strategy",
842 "resolve notes conflicts using the given "
843 "strategy (manual/ours/theirs/union)"),
844 + OPT_GROUP("Committing unmerged notes"),
845 + { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
846 + "finalize notes merge by committing unmerged notes",
847 + PARSE_OPT_NOARG | PARSE_OPT_NONEG },
848 + OPT_GROUP("Aborting notes merge resolution"),
849 + { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
850 + "abort notes merge",
851 + PARSE_OPT_NOARG | PARSE_OPT_NONEG },
852 OPT_END()
853 };
854
855 argc = parse_options(argc, argv, prefix, options,
856 git_notes_merge_usage, 0);
857
783 - if (argc != 1) {
858 + if (strategy || do_commit + do_abort == 0)
859 + do_merge = 1;
860 + if (do_merge + do_commit + do_abort != 1) {
861 + error("cannot mix --commit, --abort or -s/--strategy");
862 + usage_with_options(git_notes_merge_usage, options);
863 + }
864 +
865 + if (do_merge && argc != 1) {
866 error("Must specify a notes ref to merge");
867 usage_with_options(git_notes_merge_usage, options);
868 + } else if (!do_merge && argc) {
869 + error("too many parameters");
870 + usage_with_options(git_notes_merge_usage, options);
871 }
872
873 init_notes_merge_options(&o);
874 o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
875
876 + if (do_abort)
877 + return merge_abort(&o);
878 + if (do_commit)
879 + return merge_commit(&o);
880 +
881 o.local_ref = default_notes_ref();
882 strbuf_addstr(&remote_ref, argv[0]);
883 expand_notes_ref(&remote_ref);
@@ -820,9 +910,19 @@ static int merge(int argc, const char **argv, const char *prefix)
910 /* Update default notes ref with new commit */
911 update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
912 0, DIE_ON_ERR);
823 - else /* Merge has unresolved conflicts */
824 - printf("Automatic notes merge failed. Fix conflicts in %s.\n",
913 + else { /* Merge has unresolved conflicts */
914 + /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
915 + update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
916 + 0, DIE_ON_ERR);
917 + /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
918 + if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
919 + die("Failed to store link to current notes ref (%s)",
920 + default_notes_ref());
921 + printf("Automatic notes merge failed. Fix conflicts in %s and "
922 + "commit the result with 'git notes merge --commit', or "
923 + "abort the merge with 'git notes merge --abort'.\n",
924 git_path(NOTES_MERGE_WORKTREE));
925 + }
926
927 free_notes(t);
928 strbuf_release(&remote_ref);
notes-merge.c
+70 -1
@@ -278,7 +278,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
278 die("You have not concluded your previous "
279 "notes merge (%s exists).\nPlease, use "
280 "'git notes merge --commit' or 'git notes "
281 - "merge --reset' to commit/abort the "
281 + "merge --abort' to commit/abort the "
282 "previous merge before you start a new "
283 "notes merge.", git_path("NOTES_MERGE_*"));
284 else
@@ -650,3 +650,72 @@ found_result:
650 result, sha1_to_hex(result_sha1));
651 return result;
652 }
653 +
654 +int notes_merge_commit(struct notes_merge_options *o,
655 + struct notes_tree *partial_tree,
656 + struct commit *partial_commit,
657 + unsigned char *result_sha1)
658 +{
659 + /*
660 + * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
661 + * found notes to 'partial_tree'. Write the updates notes tree to
662 + * the DB, and commit the resulting tree object while reusing the
663 + * commit message and parents from 'partial_commit'.
664 + * Finally store the new commit object SHA1 into 'result_sha1'.
665 + */
666 + struct dir_struct dir;
667 + const char *path = git_path(NOTES_MERGE_WORKTREE "/");
668 + int path_len = strlen(path), i;
669 + const char *msg = strstr(partial_commit->buffer, "\n\n");
670 +
671 + OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s",
672 + path_len - 1, path);
673 +
674 + if (!msg || msg[2] == '\0')
675 + die("partial notes commit has empty message");
676 + msg += 2;
677 +
678 + memset(&dir, 0, sizeof(dir));
679 + read_directory(&dir, path, path_len, NULL);
680 + for (i = 0; i < dir.nr; i++) {
681 + struct dir_entry *ent = dir.entries[i];
682 + struct stat st;
683 + const char *relpath = ent->name + path_len;
684 + unsigned char obj_sha1[20], blob_sha1[20];
685 +
686 + if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
687 + OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name);
688 + continue;
689 + }
690 +
691 + /* write file as blob, and add to partial_tree */
692 + if (stat(ent->name, &st))
693 + die_errno("Failed to stat '%s'", ent->name);
694 + if (index_path(blob_sha1, ent->name, &st, 1))
695 + die("Failed to write blob object from '%s'", ent->name);
696 + if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
697 + die("Failed to add resolved note '%s' to notes tree",
698 + ent->name);
699 + OUTPUT(o, 4, "Added resolved note for object %s: %s",
700 + sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
701 + }
702 +
703 + create_notes_commit(partial_tree, partial_commit->parents, msg,
704 + result_sha1);
705 + OUTPUT(o, 4, "Finalized notes merge commit: %s",
706 + sha1_to_hex(result_sha1));
707 + return 0;
708 +}
709 +
710 +int notes_merge_abort(struct notes_merge_options *o)
711 +{
712 + /* Remove .git/NOTES_MERGE_WORKTREE directory and all files within */
713 + struct strbuf buf = STRBUF_INIT;
714 + int ret;
715 +
716 + strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
717 + OUTPUT(o, 3, "Removing notes merge worktree at %s", buf.buf);
718 + ret = remove_dir_recursively(&buf, 0);
719 + strbuf_release(&buf);
720 + return ret;
721 +}
notes-merge.h
+23
@@ -71,4 +71,27 @@ int notes_merge(struct notes_merge_options *o,
71 struct notes_tree *local_tree,
72 unsigned char *result_sha1);
73
74 +/*
75 + * Finalize conflict resolution from an earlier notes_merge()
76 + *
77 + * The given notes tree 'partial_tree' must be the notes_tree corresponding to
78 + * the given 'partial_commit', the partial result commit created by a previous
79 + * call to notes_merge().
80 + *
81 + * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE
82 + * to 'partial_tree', and create a final notes merge commit, the SHA1 of which
83 + * will be stored in 'result_sha1'.
84 + */
85 +int notes_merge_commit(struct notes_merge_options *o,
86 + struct notes_tree *partial_tree,
87 + struct commit *partial_commit,
88 + unsigned char *result_sha1);
89 +
90 +/*
91 + * Abort conflict resolution from an earlier notes_merge()
92 + *
93 + * Removes the notes merge worktree in .git/NOTES_MERGE_WORKTREE.
94 + */
95 +int notes_merge_abort(struct notes_merge_options *o);
96 +
97 #endif
t/t3310-notes-merge-manual-resolve.sh
+176
@@ -171,6 +171,7 @@ cp expect_notes_y expect_notes_m
171 cp expect_log_y expect_log_m
172
173 git rev-parse refs/notes/y > pre_merge_y
174 +git rev-parse refs/notes/z > pre_merge_z
175
176 test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
177 git update-ref refs/notes/m refs/notes/y &&
@@ -289,4 +290,179 @@ test_expect_success 'can do merge without conflicts even if previous merge is un
290 verify_notes y
291 '
292
293 +cat <<EOF | sort >expect_notes_m
294 +021faa20e931fb48986ffc6282b4bb05553ac946 $commit_sha4
295 +5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
296 +283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
297 +0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
298 +EOF
299 +
300 +cat >expect_log_m <<EOF
301 +$commit_sha5 5th
302 +
303 +$commit_sha4 4th
304 +y and z notes on 4th commit
305 +
306 +$commit_sha3 3rd
307 +y notes on 3rd commit
308 +
309 +$commit_sha2 2nd
310 +z notes on 2nd commit
311 +
312 +$commit_sha1 1st
313 +y and z notes on 1st commit
314 +
315 +EOF
316 +
317 +test_expect_success 'finalize conflicting merge (z => m)' '
318 + # Resolve conflicts and finalize merge
319 + cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
320 +y and z notes on 1st commit
321 +EOF
322 + cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
323 +y and z notes on 4th commit
324 +EOF
325 + git notes merge --commit &&
326 + # No .git/NOTES_MERGE_* files left
327 + test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
328 + test_cmp /dev/null output &&
329 + # Merge commit has pre-merge y and pre-merge z as parents
330 + test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
331 + test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
332 + # Merge commit mentions the notes refs merged
333 + git log -1 --format=%B refs/notes/m > merge_commit_msg &&
334 + grep -q refs/notes/m merge_commit_msg &&
335 + grep -q refs/notes/z merge_commit_msg &&
336 + # Verify contents of merge result
337 + verify_notes m &&
338 + # Verify that other notes refs has not changed (w, x, y and z)
339 + verify_notes w &&
340 + verify_notes x &&
341 + verify_notes y &&
342 + verify_notes z
343 +'
344 +
345 +cat >expect_conflict_$commit_sha4 <<EOF
346 +<<<<<<< refs/notes/m
347 +y notes on 4th commit
348 +=======
349 +z notes on 4th commit
350 +
351 +More z notes on 4th commit
352 +>>>>>>> refs/notes/z
353 +EOF
354 +
355 +cp expect_notes_y expect_notes_m
356 +cp expect_log_y expect_log_m
357 +
358 +git rev-parse refs/notes/y > pre_merge_y
359 +git rev-parse refs/notes/z > pre_merge_z
360 +
361 +test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
362 + git update-ref refs/notes/m refs/notes/y &&
363 + git config core.notesRef refs/notes/m &&
364 + test_must_fail git notes merge z >output &&
365 + # Output should point to where to resolve conflicts
366 + grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
367 + # Inspect merge conflicts
368 + ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
369 + test_cmp expect_conflicts output_conflicts &&
370 + ( for f in $(cat expect_conflicts); do
371 + test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
372 + exit 1
373 + done ) &&
374 + # Verify that current notes tree (pre-merge) has not changed (m == y)
375 + verify_notes y &&
376 + verify_notes m &&
377 + test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
378 +'
379 +
380 +test_expect_success 'abort notes merge' '
381 + git notes merge --abort &&
382 + # No .git/NOTES_MERGE_* files left
383 + test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
384 + test_cmp /dev/null output &&
385 + # m has not moved (still == y)
386 + test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
387 + # Verify that other notes refs has not changed (w, x, y and z)
388 + verify_notes w &&
389 + verify_notes x &&
390 + verify_notes y &&
391 + verify_notes z
392 +'
393 +
394 +git rev-parse refs/notes/y > pre_merge_y
395 +git rev-parse refs/notes/z > pre_merge_z
396 +
397 +test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
398 + test_must_fail git notes merge z >output &&
399 + # Output should point to where to resolve conflicts
400 + grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
401 + # Inspect merge conflicts
402 + ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
403 + test_cmp expect_conflicts output_conflicts &&
404 + ( for f in $(cat expect_conflicts); do
405 + test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
406 + exit 1
407 + done ) &&
408 + # Verify that current notes tree (pre-merge) has not changed (m == y)
409 + verify_notes y &&
410 + verify_notes m &&
411 + test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
412 +'
413 +
414 +cat <<EOF | sort >expect_notes_m
415 +304dfb4325cf243025b9957486eb605a9b51c199 $commit_sha5
416 +283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
417 +0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
418 +EOF
419 +
420 +cat >expect_log_m <<EOF
421 +$commit_sha5 5th
422 +new note on 5th commit
423 +
424 +$commit_sha4 4th
425 +
426 +$commit_sha3 3rd
427 +
428 +$commit_sha2 2nd
429 +z notes on 2nd commit
430 +
431 +$commit_sha1 1st
432 +y and z notes on 1st commit
433 +
434 +EOF
435 +
436 +test_expect_success 'add + remove notes in finalized merge (z => m)' '
437 + # Resolve one conflict
438 + cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
439 +y and z notes on 1st commit
440 +EOF
441 + # Remove another conflict
442 + rm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
443 + # Remove a D/F conflict
444 + rm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
445 + # Add a new note
446 + echo "new note on 5th commit" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&
447 + # Finalize merge
448 + git notes merge --commit &&
449 + # No .git/NOTES_MERGE_* files left
450 + test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
451 + test_cmp /dev/null output &&
452 + # Merge commit has pre-merge y and pre-merge z as parents
453 + test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
454 + test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
455 + # Merge commit mentions the notes refs merged
456 + git log -1 --format=%B refs/notes/m > merge_commit_msg &&
457 + grep -q refs/notes/m merge_commit_msg &&
458 + grep -q refs/notes/z merge_commit_msg &&
459 + # Verify contents of merge result
460 + verify_notes m &&
461 + # Verify that other notes refs has not changed (w, x, y and z)
462 + verify_notes w &&
463 + verify_notes x &&
464 + verify_notes y &&
465 + verify_notes z
466 +'
467 +
468 test_done