Skip to content

Commit 5b6211a

Browse files
committed
Merge branch 'jk/notes-merge-config'
"git notes merge" can be told with "--strategy=<how>" option how to automatically handle conflicts; this can now be configured by setting notes.mergeStrategy configuration variable. * jk/notes-merge-config: notes: teach git-notes about notes.<name>.mergeStrategy option notes: add notes.mergeStrategy option to select default strategy notes: add tests for --commit/--abort/--strategy exclusivity notes: extract parse_notes_merge_strategy to notes-utils notes: extract enum notes_merge_strategy to notes-utils.h notes: document cat_sort_uniq rewriteMode
2 parents d75bb73 + 4f655e2 commit 5b6211a

File tree

8 files changed

+187
-25
lines changed

8 files changed

+187
-25
lines changed

Documentation/config.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,6 +1942,18 @@ mergetool.writeToTemp::
19421942
mergetool.prompt::
19431943
Prompt before each invocation of the merge resolution program.
19441944

1945+
notes.mergeStrategy::
1946+
Which merge strategy to choose by default when resolving notes
1947+
conflicts. Must be one of `manual`, `ours`, `theirs`, `union`, or
1948+
`cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE STRATEGIES"
1949+
section of linkgit:git-notes[1] for more information on each strategy.
1950+
1951+
notes.<name>.mergeStrategy::
1952+
Which merge strategy to choose when doing a notes merge into
1953+
refs/notes/<name>. This overrides the more general
1954+
"notes.mergeStrategy". See the "NOTES MERGE STRATEGIES" section in
1955+
linkgit:git-notes[1] for more information on the available strategies.
1956+
19451957
notes.displayRef::
19461958
The (fully qualified) refname from which to show notes when
19471959
showing commit messages. The value of this variable can be set
@@ -1970,8 +1982,8 @@ notes.rewriteMode::
19701982
When copying notes during a rewrite (see the
19711983
"notes.rewrite.<command>" option), determines what to do if
19721984
the target commit already has a note. Must be one of
1973-
`overwrite`, `concatenate`, or `ignore`. Defaults to
1974-
`concatenate`.
1985+
`overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
1986+
Defaults to `concatenate`.
19751987
+
19761988
This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
19771989
environment variable.

Documentation/git-notes.txt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ merge::
101101
any) into the current notes ref (called "local").
102102
+
103103
If conflicts arise and a strategy for automatically resolving
104-
conflicting notes (see the -s/--strategy option) is not given,
104+
conflicting notes (see the "NOTES MERGE STRATEGIES" section) is not given,
105105
the "manual" resolver is used. This resolver checks out the
106106
conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
107107
and instructs the user to manually resolve the conflicts there.
@@ -183,6 +183,7 @@ OPTIONS
183183
When merging notes, resolve notes conflicts using the given
184184
strategy. The following strategies are recognized: "manual"
185185
(default), "ours", "theirs", "union" and "cat_sort_uniq".
186+
This option overrides the "notes.mergeStrategy" configuration setting.
186187
See the "NOTES MERGE STRATEGIES" section below for more
187188
information on each notes merge strategy.
188189

@@ -247,6 +248,9 @@ When done, the user can either finalize the merge with
247248
'git notes merge --commit', or abort the merge with
248249
'git notes merge --abort'.
249250

251+
Users may select an automated merge strategy from among the following using
252+
either -s/--strategy option or configuring notes.mergeStrategy accordingly:
253+
250254
"ours" automatically resolves conflicting notes in favor of the local
251255
version (i.e. the current notes ref).
252256

@@ -310,6 +314,20 @@ core.notesRef::
310314
This setting can be overridden through the environment and
311315
command line.
312316

317+
notes.mergeStrategy::
318+
Which merge strategy to choose by default when resolving notes
319+
conflicts. Must be one of `manual`, `ours`, `theirs`, `union`, or
320+
`cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE STRATEGIES"
321+
section above for more information on each strategy.
322+
+
323+
This setting can be overridden by passing the `--strategy` option.
324+
325+
notes.<name>.mergeStrategy::
326+
Which merge strategy to choose when doing a notes merge into
327+
refs/notes/<name>. This overrides the more general
328+
"notes.mergeStrategy". See the "NOTES MERGE STRATEGIES" section above
329+
for more information on each available strategy.
330+
313331
notes.displayRef::
314332
Which ref (or refs, if a glob or specified more than once), in
315333
addition to the default set by `core.notesRef` or
@@ -331,7 +349,8 @@ environment variable.
331349
notes.rewriteMode::
332350
When copying notes during a rewrite, what to do if the target
333351
commit already has a note. Must be one of `overwrite`,
334-
`concatenate`, and `ignore`. Defaults to `concatenate`.
352+
`concatenate`, `cat_sort_uniq`, or `ignore`. Defaults to
353+
`concatenate`.
335354
+
336355
This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
337356
environment variable.
@@ -368,7 +387,7 @@ does not match any refs is silently ignored.
368387
'GIT_NOTES_REWRITE_MODE'::
369388
When copying notes during a rewrite, what to do if the target
370389
commit already has a note.
371-
Must be one of `overwrite`, `concatenate`, and `ignore`.
390+
Must be one of `overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
372391
This overrides the `core.rewriteMode` setting.
373392

374393
'GIT_NOTES_REWRITE_REF'::

builtin/notes.c

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,19 @@ static int merge_commit(struct notes_merge_options *o)
738738
return ret;
739739
}
740740

741+
static int git_config_get_notes_strategy(const char *key,
742+
enum notes_merge_strategy *strategy)
743+
{
744+
const char *value;
745+
746+
if (git_config_get_string_const(key, &value))
747+
return 1;
748+
if (parse_notes_merge_strategy(value, strategy))
749+
git_die_config(key, "unknown notes merge strategy %s", value);
750+
751+
return 0;
752+
}
753+
741754
static int merge(int argc, const char **argv, const char *prefix)
742755
{
743756
struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
@@ -796,24 +809,28 @@ static int merge(int argc, const char **argv, const char *prefix)
796809
expand_notes_ref(&remote_ref);
797810
o.remote_ref = remote_ref.buf;
798811

812+
t = init_notes_check("merge");
813+
799814
if (strategy) {
800-
if (!strcmp(strategy, "manual"))
801-
o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
802-
else if (!strcmp(strategy, "ours"))
803-
o.strategy = NOTES_MERGE_RESOLVE_OURS;
804-
else if (!strcmp(strategy, "theirs"))
805-
o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
806-
else if (!strcmp(strategy, "union"))
807-
o.strategy = NOTES_MERGE_RESOLVE_UNION;
808-
else if (!strcmp(strategy, "cat_sort_uniq"))
809-
o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
810-
else {
815+
if (parse_notes_merge_strategy(strategy, &o.strategy)) {
811816
error("Unknown -s/--strategy: %s", strategy);
812817
usage_with_options(git_notes_merge_usage, options);
813818
}
814-
}
819+
} else {
820+
struct strbuf merge_key = STRBUF_INIT;
821+
const char *short_ref = NULL;
815822

816-
t = init_notes_check("merge");
823+
if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
824+
die("BUG: local ref %s is outside of refs/notes/",
825+
o.local_ref);
826+
827+
strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref);
828+
829+
if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
830+
git_config_get_notes_strategy("notes.mergeStrategy", &o.strategy);
831+
832+
strbuf_release(&merge_key);
833+
}
817834

818835
strbuf_addf(&msg, "notes: Merged notes from %s into %s",
819836
remote_ref.buf, default_notes_ref());

notes-merge.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef NOTES_MERGE_H
22
#define NOTES_MERGE_H
33

4+
#include "notes-utils.h"
5+
46
#define NOTES_MERGE_WORKTREE "NOTES_MERGE_WORKTREE"
57

68
enum notes_merge_verbosity {
@@ -13,13 +15,7 @@ struct notes_merge_options {
1315
const char *remote_ref;
1416
struct strbuf commit_msg;
1517
int verbosity;
16-
enum {
17-
NOTES_MERGE_RESOLVE_MANUAL = 0,
18-
NOTES_MERGE_RESOLVE_OURS,
19-
NOTES_MERGE_RESOLVE_THEIRS,
20-
NOTES_MERGE_RESOLVE_UNION,
21-
NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
22-
} strategy;
18+
enum notes_merge_strategy strategy;
2319
unsigned has_worktree:1;
2420
};
2521

notes-utils.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ void commit_notes(struct notes_tree *t, const char *msg)
5454
strbuf_release(&buf);
5555
}
5656

57+
int parse_notes_merge_strategy(const char *v, enum notes_merge_strategy *s)
58+
{
59+
if (!strcmp(v, "manual"))
60+
*s = NOTES_MERGE_RESOLVE_MANUAL;
61+
else if (!strcmp(v, "ours"))
62+
*s = NOTES_MERGE_RESOLVE_OURS;
63+
else if (!strcmp(v, "theirs"))
64+
*s = NOTES_MERGE_RESOLVE_THEIRS;
65+
else if (!strcmp(v, "union"))
66+
*s = NOTES_MERGE_RESOLVE_UNION;
67+
else if (!strcmp(v, "cat_sort_uniq"))
68+
*s = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
69+
else
70+
return -1;
71+
72+
return 0;
73+
}
74+
5775
static combine_notes_fn parse_combine_notes_fn(const char *v)
5876
{
5977
if (!strcasecmp(v, "overwrite"))

notes-utils.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
1919

2020
void commit_notes(struct notes_tree *t, const char *msg);
2121

22+
enum notes_merge_strategy {
23+
NOTES_MERGE_RESOLVE_MANUAL = 0,
24+
NOTES_MERGE_RESOLVE_OURS,
25+
NOTES_MERGE_RESOLVE_THEIRS,
26+
NOTES_MERGE_RESOLVE_UNION,
27+
NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
28+
};
29+
2230
struct notes_rewrite_cfg {
2331
struct notes_tree **trees;
2432
const char *cmd;
@@ -29,6 +37,7 @@ struct notes_rewrite_cfg {
2937
int mode_from_env;
3038
};
3139

40+
int parse_notes_merge_strategy(const char *v, enum notes_merge_strategy *s);
3241
struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
3342
int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
3443
const unsigned char *from_obj, const unsigned char *to_obj);

t/t3309-notes-merge-auto-resolve.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
298298
verify_notes y y
299299
'
300300

301+
test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
302+
git config core.notesRef refs/notes/y &&
303+
test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
304+
# Verify no changes (y)
305+
verify_notes y y
306+
'
307+
301308
cat <<EOF | sort >expect_notes_ours
302309
68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
303310
5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -365,6 +372,28 @@ test_expect_success 'reset to pre-merge state (y)' '
365372
verify_notes y y
366373
'
367374

375+
test_expect_success 'merge z into y with "ours" configuration option => Non-conflicting 3-way merge' '
376+
git -c notes.mergeStrategy="ours" notes merge z &&
377+
verify_notes y ours
378+
'
379+
380+
test_expect_success 'reset to pre-merge state (y)' '
381+
git update-ref refs/notes/y refs/notes/y^1 &&
382+
# Verify pre-merge state
383+
verify_notes y y
384+
'
385+
386+
test_expect_success 'merge z into y with "ours" per-ref configuration option => Non-conflicting 3-way merge' '
387+
git -c notes.y.mergeStrategy="ours" notes merge z &&
388+
verify_notes y ours
389+
'
390+
391+
test_expect_success 'reset to pre-merge state (y)' '
392+
git update-ref refs/notes/y refs/notes/y^1 &&
393+
# Verify pre-merge state
394+
verify_notes y y
395+
'
396+
368397
cat <<EOF | sort >expect_notes_theirs
369398
9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
370399
5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -432,6 +461,17 @@ test_expect_success 'reset to pre-merge state (y)' '
432461
verify_notes y y
433462
'
434463

464+
test_expect_success 'merge z into y with "theirs" strategy overriding configuration option "ours" => Non-conflicting 3-way merge' '
465+
git -c notes.mergeStrategy="ours" notes merge --strategy=theirs z &&
466+
verify_notes y theirs
467+
'
468+
469+
test_expect_success 'reset to pre-merge state (y)' '
470+
git update-ref refs/notes/y refs/notes/y^1 &&
471+
# Verify pre-merge state
472+
verify_notes y y
473+
'
474+
435475
cat <<EOF | sort >expect_notes_union
436476
7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15
437477
5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -505,6 +545,34 @@ test_expect_success 'reset to pre-merge state (y)' '
505545
verify_notes y y
506546
'
507547

548+
test_expect_success 'merge z into y with "union" strategy overriding per-ref configuration => Non-conflicting 3-way merge' '
549+
git -c notes.y.mergeStrategy="theirs" notes merge --strategy=union z &&
550+
verify_notes y union
551+
'
552+
553+
test_expect_success 'reset to pre-merge state (y)' '
554+
git update-ref refs/notes/y refs/notes/y^1 &&
555+
# Verify pre-merge state
556+
verify_notes y y
557+
'
558+
559+
test_expect_success 'merge z into y with "union" per-ref overriding general configuration => Non-conflicting 3-way merge' '
560+
git -c notes.y.mergeStrategy="union" -c notes.mergeStrategy="theirs" notes merge z &&
561+
verify_notes y union
562+
'
563+
564+
test_expect_success 'reset to pre-merge state (y)' '
565+
git update-ref refs/notes/y refs/notes/y^1 &&
566+
# Verify pre-merge state
567+
verify_notes y y
568+
'
569+
570+
test_expect_success 'merge z into y with "manual" per-ref only checks specific ref configuration => Conflicting 3-way merge' '
571+
test_must_fail git -c notes.z.mergeStrategy="union" notes merge z &&
572+
git notes merge --abort &&
573+
verify_notes y y
574+
'
575+
508576
cat <<EOF | sort >expect_notes_union2
509577
d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
510578
5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -644,4 +712,15 @@ test_expect_success 'merge y into z with "cat_sort_uniq" strategy => Non-conflic
644712
verify_notes z cat_sort_uniq
645713
'
646714

715+
test_expect_success 'reset to pre-merge state (z)' '
716+
git update-ref refs/notes/z refs/notes/z^1 &&
717+
# Verify pre-merge state
718+
verify_notes z z
719+
'
720+
721+
test_expect_success 'merge y into z with "cat_sort_uniq" strategy configuration option => Non-conflicting 3-way merge' '
722+
git -c notes.mergeStrategy="cat_sort_uniq" notes merge y &&
723+
verify_notes z cat_sort_uniq
724+
'
725+
647726
test_done

t/t3310-notes-merge-manual-resolve.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,18 @@ y and z notes on 1st commit
314314
315315
EOF
316316

317+
test_expect_success 'do not allow mixing --commit and --abort' '
318+
test_must_fail git notes merge --commit --abort
319+
'
320+
321+
test_expect_success 'do not allow mixing --commit and --strategy' '
322+
test_must_fail git notes merge --commit --strategy theirs
323+
'
324+
325+
test_expect_success 'do not allow mixing --abort and --strategy' '
326+
test_must_fail git notes merge --abort --strategy theirs
327+
'
328+
317329
test_expect_success 'finalize conflicting merge (z => m)' '
318330
# Resolve conflicts and finalize merge
319331
cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&

0 commit comments

Comments
 (0)