threads / patch / 40092

v7, 4 partsnotes: document cat_sort_uniq rewriteMode

Subject: [PATCH v7 1/4] notes: document cat_sort_uniq rewriteMode

## tl;dr

16 messages between Aug 14, 2015 and Aug 17, 2015. Diffs are folded; open one to read it.

replies: 15people: 5as markdown or json

Jacob Keller· Aug 14, 2015, 21:13 UTC · lore

[PATCH v7 0/4] notes.mergestrategy option(s)

From: Jacob Keller <jacob.keller@gmail.com>
Changes since v6
* Eric suggested a more stream-lined approach for
  git_config_get_notes_strategy
Jacob Keller (4):
  notes: document cat_sort_uniq rewriteMode
  notes: add tests for --commit/--abort/--strategy exclusivity
  notes: add notes.mergestrategy option to select default strategy
  notes: teach git-notes about notes.<ref>.mergestrategy option
 Documentation/config.txt              | 18 ++++++++--
 Documentation/git-notes.txt           | 23 ++++++++++--
 builtin/notes.c                       | 56 ++++++++++++++++++++++-------
 notes-merge.h                         | 16 +++++----
 t/t3309-notes-merge-auto-resolve.sh   | 68 +++++++++++++++++++++++++++++++++++
 t/t3310-notes-merge-manual-resolve.sh | 12 +++++++
 6 files changed, 169 insertions(+), 24 deletions(-)
-- 
2.5.0.280.g4aaba03
Jacob Keller· Aug 14, 2015, 21:13 UTC · re: Jacob Keller · lore
From: Jacob Keller <jacob.keller@gmail.com>

Teach documentation about the cat_sort_uniq rewriteMode that got added at the same time as the equivalent merge strategy.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 Documentation/config.txt    | 4 ++--
 Documentation/git-notes.txt | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)
Show changes to 2 files +4 −3

Documentation/config.txt, Documentation/git-notes.txt

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 75ec02e8e90a..de67ad1fdedf 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1947,8 +1947,8 @@ notes.rewriteMode::
 	When copying notes during a rewrite (see the
 	"notes.rewrite.<command>" option), determines what to do if
 	the target commit already has a note.  Must be one of
-	`overwrite`, `concatenate`, or `ignore`.  Defaults to
-	`concatenate`.
+	`overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
+	Defaults to `concatenate`.
 +
 This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
 environment variable.
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 851518d531b5..674682b34b83 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -331,7 +331,8 @@ environment variable.
 notes.rewriteMode::
 	When copying notes during a rewrite, what to do if the target
 	commit already has a note.  Must be one of `overwrite`,
-	`concatenate`, and `ignore`.  Defaults to `concatenate`.
+	`concatenate`, `cat_sort_uniq`, or `ignore`.  Defaults to
+	`concatenate`.
 +
 This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
 environment variable.
-- 
2.5.0.280.g4aaba03
Junio C Hamano· Aug 14, 2015, 22:11 UTC · re: Jacob Keller · lore

Re: [PATCH v7 1/4] notes: document cat_sort_uniq rewriteMode

Jacob Keller <jacob.e.keller@intel.com> writes:
Show 29 quoted lines
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 75ec02e8e90a..de67ad1fdedf 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -1947,8 +1947,8 @@ notes.rewriteMode::
>  	When copying notes during a rewrite (see the
>  	"notes.rewrite.<command>" option), determines what to do if
>  	the target commit already has a note.  Must be one of
> -	`overwrite`, `concatenate`, or `ignore`.  Defaults to
> -	`concatenate`.
> +	`overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
> +	Defaults to `concatenate`.
>  +
>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>  environment variable.
> diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
> index 851518d531b5..674682b34b83 100644
> --- a/Documentation/git-notes.txt
> +++ b/Documentation/git-notes.txt
> @@ -331,7 +331,8 @@ environment variable.
>  notes.rewriteMode::
>  	When copying notes during a rewrite, what to do if the target
>  	commit already has a note.  Must be one of `overwrite`,
> -	`concatenate`, and `ignore`.  Defaults to `concatenate`.
> +	`concatenate`, `cat_sort_uniq`, or `ignore`.  Defaults to
> +	`concatenate`.
>  +
>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>  environment variable.

This obviously is not a problem introduced by this patch, but I wonder why we have two similar but different set of modes for rewrtie and merge. Isn't 'overwrite' like 'ours', 'ignore' like 'theirs', and 'concat' like 'union', and if these are similar enough, perhaps it would be helpful to the end user if we unified the terms (or accepted both as synonyms for backward compatibility)?

Also I notice that you cannot manually reconcile while rewriting; don't we want to have 'manual' there, too, I wonder?

[jc: Cc'ed Thomas who invented rewrite back when merge was not even there, and Johan who added merge]

Jacob Keller· Aug 14, 2015, 22:53 UTC · re: Junio C Hamano · lore

Re: [PATCH v7 1/4] notes: document cat_sort_uniq rewriteMode

On Fri, Aug 14, 2015 at 3:11 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 45 quoted lines
> Jacob Keller <jacob.e.keller@intel.com> writes:
>
>> diff --git a/Documentation/config.txt b/Documentation/config.txt
>> index 75ec02e8e90a..de67ad1fdedf 100644
>> --- a/Documentation/config.txt
>> +++ b/Documentation/config.txt
>> @@ -1947,8 +1947,8 @@ notes.rewriteMode::
>>       When copying notes during a rewrite (see the
>>       "notes.rewrite.<command>" option), determines what to do if
>>       the target commit already has a note.  Must be one of
>> -     `overwrite`, `concatenate`, or `ignore`.  Defaults to
>> -     `concatenate`.
>> +     `overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
>> +     Defaults to `concatenate`.
>>  +
>>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>>  environment variable.
>> diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
>> index 851518d531b5..674682b34b83 100644
>> --- a/Documentation/git-notes.txt
>> +++ b/Documentation/git-notes.txt
>> @@ -331,7 +331,8 @@ environment variable.
>>  notes.rewriteMode::
>>       When copying notes during a rewrite, what to do if the target
>>       commit already has a note.  Must be one of `overwrite`,
>> -     `concatenate`, and `ignore`.  Defaults to `concatenate`.
>> +     `concatenate`, `cat_sort_uniq`, or `ignore`.  Defaults to
>> +     `concatenate`.
>>  +
>>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>>  environment variable.
>
> This obviously is not a problem introduced by this patch, but I
> wonder why we have two similar but different set of modes for
> rewrtie and merge.  Isn't 'overwrite' like 'ours', 'ignore' like
> 'theirs', and 'concat' like 'union', and if these are similar
> enough, perhaps it would be helpful to the end user if we unified
> the terms (or accepted both as synonyms for backward compatibility)?
>
> Also I notice that you cannot manually reconcile while rewriting;
> don't we want to have 'manual' there, too, I wonder?
>
> [jc: Cc'ed Thomas who invented rewrite back when merge was not even
> there, and Johan who added merge]
>

I was not sure. I believe that re-write doesn't do the same thing as merge? I think we could make all of them handle the "overwrite", which is basically a synonym of, I think "theirs" depending on the direction of the "merge".

I don't know if re-write actually supports manual mode at all!

Maybe we could make merge support the other names as synonyms, and then code re-write in terms of merging?

I wasn't sure so I chose only to document the mode that was missing.

Regards, Jake

Johan Herland· Aug 15, 2015, 10:06 UTC · re: Jacob Keller · lore

Re: [PATCH v7 1/4] notes: document cat_sort_uniq rewriteMode

On Sat, Aug 15, 2015 at 12:53 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
Show 36 quoted lines
> On Fri, Aug 14, 2015 at 3:11 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Jacob Keller <jacob.e.keller@intel.com> writes:
>>
>>> diff --git a/Documentation/config.txt b/Documentation/config.txt
>>> index 75ec02e8e90a..de67ad1fdedf 100644
>>> --- a/Documentation/config.txt
>>> +++ b/Documentation/config.txt
>>> @@ -1947,8 +1947,8 @@ notes.rewriteMode::
>>>       When copying notes during a rewrite (see the
>>>       "notes.rewrite.<command>" option), determines what to do if
>>>       the target commit already has a note.  Must be one of
>>> -     `overwrite`, `concatenate`, or `ignore`.  Defaults to
>>> -     `concatenate`.
>>> +     `overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.
>>> +     Defaults to `concatenate`.
>>>  +
>>>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>>>  environment variable.
>>> diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
>>> index 851518d531b5..674682b34b83 100644
>>> --- a/Documentation/git-notes.txt
>>> +++ b/Documentation/git-notes.txt
>>> @@ -331,7 +331,8 @@ environment variable.
>>>  notes.rewriteMode::
>>>       When copying notes during a rewrite, what to do if the target
>>>       commit already has a note.  Must be one of `overwrite`,
>>> -     `concatenate`, and `ignore`.  Defaults to `concatenate`.
>>> +     `concatenate`, `cat_sort_uniq`, or `ignore`.  Defaults to
>>> +     `concatenate`.
>>>  +
>>>  This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
>>>  environment variable.
>>
>> This obviously is not a problem introduced by this patch, but I
>> wonder why we have two similar but different set of modes for
>> rewrtie and merge.

We do. Rewrite builds directly on top of the combine_notes_* functions that are part of the core/low-level notes code, added in 73f464b5 (2010-02-13, Refactor notes concatenation into a flexible interface for combining notes). Notes merge (and its various strategies) were added later, and also build on top of these combine_notes_* functions (see merge_one_change() in notes-merge.c). In addition, notes-merge adds the 'manual' strategy, which depends on surrounding machinery that notes-merge provides, but that the low-level combine_notes_* functions cannot depend on.

>>  Isn't 'overwrite' like 'ours', 'ignore' like
>> 'theirs', and 'concat' like 'union', and if these are similar
>> enough, perhaps it would be helpful to the end user if we unified
>> the terms (or accepted both as synonyms for backward compatibility)?

The mapping (which is contained in merge_one_change() in notes-merge.c) is:

  'manual'        -> [custom handling in notes-merge]
  'ours'          -> combine_notes_ignore [or simply a no-op]
  'theirs'        -> combine_notes_overwrite
  'union'         -> combine_notes_concatenate
  'cat_sort_uniq' -> combine_notes_cat_sort_uniq
>>
>> Also I notice that you cannot manually reconcile while rewriting;
>> don't we want to have 'manual' there, too, I wonder?

No, as stated above, 'manual' requires some external mechanism for creating a "worktree" where the notes conflicts can be checked out and manipulated by the user. The guts of notes.c is not the correct place to do that. I don't know if it's easy to rewrite "rewrite" to do a (partial) notes merge instead of using the combine_notes_* functions directly, but I imagine that would be the best way forward.

Show 9 quoted lines
>>
>> [jc: Cc'ed Thomas who invented rewrite back when merge was not even
>> there, and Johan who added merge]
>>
>
> I was not sure. I believe that re-write doesn't do the same thing as
> merge? I think we could make all of them handle the "overwrite", which
> is basically a synonym of, I think "theirs" depending on the direction
> of the "merge".
Correct.
> I don't know if re-write actually supports manual mode at all!
It doesn't.
> Maybe we could make merge support the other names as synonyms, and
> then code re-write in terms of merging?

Adding the synonyms is fine, but I wouldn't publicize it heavily, as the meaning of words like "overwrite" and "ignore" can become quite confusing in the context of a merge.

Reimplementing re-write in terms of merge is probably a good idea, though.

> I wasn't sure so I chose only to document the mode that was missing.
IMHO, that's good for now.
...Johan
-- 
Johan Herland, <johan@herland.net>
www.herland.net
Jacob Keller· Aug 14, 2015, 21:13 UTC · re: Jacob Keller · lore

[PATCH v7 2/4] notes: add tests for --commit/--abort/--strategy exclusivity

From: Jacob Keller <jacob.keller@gmail.com>

Add new tests to ensure that --commit, --abort, and --strategy are mutually exclusive.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 t/t3310-notes-merge-manual-resolve.sh | 12 ++++++++++++
 1 file changed, 12 insertions(+)
Show changes to t/t3310-notes-merge-manual-resolve.sh +12 −0
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 195bb97f859d..d5572121da69 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -314,6 +314,18 @@ y and z notes on 1st commit
 
 EOF
 
+test_expect_success 'do not allow mixing --commit and --abort' '
+	test_must_fail git notes merge --commit --abort
+'
+
+test_expect_success 'do not allow mixing --commit and --strategy' '
+	test_must_fail git notes merge --commit --strategy theirs
+'
+
+test_expect_success 'do not allow mixing --abort and --strategy' '
+	test_must_fail git notes merge --abort --strategy theirs
+'
+
 test_expect_success 'finalize conflicting merge (z => m)' '
 	# Resolve conflicts and finalize merge
 	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
-- 
2.5.0.280.g4aaba03
Jacob Keller· Aug 14, 2015, 21:13 UTC · re: Jacob Keller · lore

[PATCH v7 3/4] notes: add notes.mergestrategy option to select default strategy

From: Jacob Keller <jacob.keller@gmail.com>

Teach git-notes about "notes.mergestrategy" to select a general strategy for all notes merges. This enables a user to always get expected merge strategy such as "cat_sort_uniq" without having to pass the "-s" option manually.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 Documentation/config.txt            |  7 ++++++
 Documentation/git-notes.txt         | 14 +++++++++++-
 builtin/notes.c                     | 45 ++++++++++++++++++++++++++++---------
 notes-merge.h                       | 16 +++++++------
 t/t3309-notes-merge-auto-resolve.sh | 29 ++++++++++++++++++++++++
 5 files changed, 92 insertions(+), 19 deletions(-)
Show changes to 5 files +92 −19

Documentation/config.txt, Documentation/git-notes.txt, builtin/notes.c, notes-merge.h, t/t3309-notes-merge-auto-resolve.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index de67ad1fdedf..5e3e03459de7 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1919,6 +1919,13 @@ mergetool.writeToTemp::
 mergetool.prompt::
 	Prompt before each invocation of the merge resolution program.
 
+notes.mergestrategy::
+	Which merge strategy to choose by default when resolving notes
+	conflicts. Must be one of `manual`, `ours`, `theirs`, `union`,
+	or `cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE
+	STRATEGIES" section of linkgit:git-notes[1] for more information
+	on each strategy.
+
 notes.displayRef::
 	The (fully qualified) refname from which to show notes when
 	showing commit messages.  The value of this variable can be set
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 674682b34b83..89c8829a0543 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -101,7 +101,7 @@ merge::
 	any) into the current notes ref (called "local").
 +
 If conflicts arise and a strategy for automatically resolving
-conflicting notes (see the -s/--strategy option) is not given,
+conflicting notes (see the "NOTES MERGE STRATEGIES" section) is not given,
 the "manual" resolver is used. This resolver checks out the
 conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
 and instructs the user to manually resolve the conflicts there.
@@ -183,6 +183,7 @@ OPTIONS
 	When merging notes, resolve notes conflicts using the given
 	strategy. The following strategies are recognized: "manual"
 	(default), "ours", "theirs", "union" and "cat_sort_uniq".
+	This option overrides the "notes.mergestrategy" configuration setting.
 	See the "NOTES MERGE STRATEGIES" section below for more
 	information on each notes merge strategy.
 
@@ -247,6 +248,9 @@ When done, the user can either finalize the merge with
 'git notes merge --commit', or abort the merge with
 'git notes merge --abort'.
 
+Users may select an automated merge strategy from among the following using
+either -s/--strategy option or configuring notes.mergestrategy accordingly:
+
 "ours" automatically resolves conflicting notes in favor of the local
 version (i.e. the current notes ref).
 
@@ -310,6 +314,14 @@ core.notesRef::
 	This setting can be overridden through the environment and
 	command line.
 
+notes.mergestrategy::
+	Which merge strategy to choose by default when resolving notes
+	conflicts. Must be one of `manual`, `ours`, `theirs`, `union`,
+	or `cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE
+	STRATEGIES" section above for more information on each strategy.
++
+This setting can be overridden by passing the `--strategy` option.
+
 notes.displayRef::
 	Which ref (or refs, if a glob or specified more than once), in
 	addition to the default set by `core.notesRef` or
diff --git a/builtin/notes.c b/builtin/notes.c
index 042348082709..12a42b583f98 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -738,6 +738,37 @@ static int merge_commit(struct notes_merge_options *o)
 	return ret;
 }
 
+static int parse_notes_strategy(const char *arg, enum notes_merge_strategy *strategy)
+{
+	if (!strcmp(arg, "manual"))
+		*strategy = NOTES_MERGE_RESOLVE_MANUAL;
+	else if (!strcmp(arg, "ours"))
+		*strategy = NOTES_MERGE_RESOLVE_OURS;
+	else if (!strcmp(arg, "theirs"))
+		*strategy = NOTES_MERGE_RESOLVE_THEIRS;
+	else if (!strcmp(arg, "union"))
+		*strategy = NOTES_MERGE_RESOLVE_UNION;
+	else if (!strcmp(arg, "cat_sort_uniq"))
+		*strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
+	else
+		return -1;
+
+	return 0;
+}
+
+static int git_config_get_notes_strategy(const char *key,
+					 enum notes_merge_strategy *strategy)
+{
+	const char *value;
+
+	if (git_config_get_string_const(key, &value))
+		return 1;
+	if (parse_notes_strategy(value, strategy))
+		git_die_config(key, "unknown notes merge strategy %s", value);
+
+	return 0;
+}
+
 static int merge(int argc, const char **argv, const char *prefix)
 {
 	struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
@@ -797,20 +828,12 @@ static int merge(int argc, const char **argv, const char *prefix)
 	o.remote_ref = remote_ref.buf;
 
 	if (strategy) {
-		if (!strcmp(strategy, "manual"))
-			o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
-		else if (!strcmp(strategy, "ours"))
-			o.strategy = NOTES_MERGE_RESOLVE_OURS;
-		else if (!strcmp(strategy, "theirs"))
-			o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
-		else if (!strcmp(strategy, "union"))
-			o.strategy = NOTES_MERGE_RESOLVE_UNION;
-		else if (!strcmp(strategy, "cat_sort_uniq"))
-			o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
-		else {
+		if (parse_notes_strategy(strategy, &o.strategy)) {
 			error("Unknown -s/--strategy: %s", strategy);
 			usage_with_options(git_notes_merge_usage, options);
 		}
+	} else {
+		git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
 	}
 
 	t = init_notes_check("merge");
diff --git a/notes-merge.h b/notes-merge.h
index 1d01f6aacf54..bda8c0c8d348 100644
--- a/notes-merge.h
+++ b/notes-merge.h
@@ -8,18 +8,20 @@ enum notes_merge_verbosity {
 	NOTES_MERGE_VERBOSITY_MAX = 5
 };
 
+enum notes_merge_strategy {
+	NOTES_MERGE_RESOLVE_MANUAL = 0,
+	NOTES_MERGE_RESOLVE_OURS,
+	NOTES_MERGE_RESOLVE_THEIRS,
+	NOTES_MERGE_RESOLVE_UNION,
+	NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
+};
+
 struct notes_merge_options {
 	const char *local_ref;
 	const char *remote_ref;
 	struct strbuf commit_msg;
 	int verbosity;
-	enum {
-		NOTES_MERGE_RESOLVE_MANUAL = 0,
-		NOTES_MERGE_RESOLVE_OURS,
-		NOTES_MERGE_RESOLVE_THEIRS,
-		NOTES_MERGE_RESOLVE_UNION,
-		NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
-	} strategy;
+	enum notes_merge_strategy strategy;
 	unsigned has_worktree:1;
 };
 
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 461fd84755d7..476b9f5306f1 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -298,6 +298,13 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
 	verify_notes y y
 '
 
+test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
+	git config core.notesRef refs/notes/y &&
+	test_must_fail git -c notes.mergestrategy="foo" notes merge z &&
+	# Verify no changes (y)
+	verify_notes y y
+'
+
 cat <<EOF | sort >expect_notes_ours
 68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -365,6 +372,17 @@ test_expect_success 'reset to pre-merge state (y)' '
 	verify_notes y y
 '
 
+test_expect_success 'merge z into y with "ours" configuration option => Non-conflicting 3-way merge' '
+	git -c notes.mergestrategy="ours" notes merge z &&
+	verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
 cat <<EOF | sort >expect_notes_theirs
 9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -432,6 +450,17 @@ test_expect_success 'reset to pre-merge state (y)' '
 	verify_notes y y
 '
 
+test_expect_success 'merge z into y with "theirs" strategy overriding configuration option "ours" => Non-conflicting 3-way merge' '
+	git -c notes.mergestrategy="ours" notes merge --strategy=theirs z &&
+	verify_notes y theirs
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
 cat <<EOF | sort >expect_notes_union
 7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15
 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
-- 
2.5.0.280.g4aaba03
Johan Herland· Aug 15, 2015, 09:09 UTC · re: Jacob Keller · lore

Re: [PATCH v7 3/4] notes: add notes.mergestrategy option to select default strategy

On Fri, Aug 14, 2015 at 11:13 PM, Jacob Keller <jacob.e.keller@intel.com> wrote:
Show 25 quoted lines
> From: Jacob Keller <jacob.keller@gmail.com>
>
> Teach git-notes about "notes.mergestrategy" to select a general strategy
> for all notes merges. This enables a user to always get expected merge
> strategy such as "cat_sort_uniq" without having to pass the "-s" option
> manually.
>
> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> ---
>  Documentation/config.txt            |  7 ++++++
>  Documentation/git-notes.txt         | 14 +++++++++++-
>  builtin/notes.c                     | 45 ++++++++++++++++++++++++++++---------
>  notes-merge.h                       | 16 +++++++------
>  t/t3309-notes-merge-auto-resolve.sh | 29 ++++++++++++++++++++++++
>  5 files changed, 92 insertions(+), 19 deletions(-)
>
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index de67ad1fdedf..5e3e03459de7 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -1919,6 +1919,13 @@ mergetool.writeToTemp::
>  mergetool.prompt::
>         Prompt before each invocation of the merge resolution program.
>
> +notes.mergestrategy::

Just one small nit: Config keys are (AFAIK) case-insensitive, and we can thus use camelCasing in the documentation to make long keywords more readable (see e.g. notes.displayRef below). So I suggest using notes.mergeStrategy here.

Show 45 quoted lines
> +       Which merge strategy to choose by default when resolving notes
> +       conflicts. Must be one of `manual`, `ours`, `theirs`, `union`,
> +       or `cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE
> +       STRATEGIES" section of linkgit:git-notes[1] for more information
> +       on each strategy.
> +
>  notes.displayRef::
>         The (fully qualified) refname from which to show notes when
>         showing commit messages.  The value of this variable can be set
> diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
> index 674682b34b83..89c8829a0543 100644
> --- a/Documentation/git-notes.txt
> +++ b/Documentation/git-notes.txt
> @@ -101,7 +101,7 @@ merge::
>         any) into the current notes ref (called "local").
>  +
>  If conflicts arise and a strategy for automatically resolving
> -conflicting notes (see the -s/--strategy option) is not given,
> +conflicting notes (see the "NOTES MERGE STRATEGIES" section) is not given,
>  the "manual" resolver is used. This resolver checks out the
>  conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
>  and instructs the user to manually resolve the conflicts there.
> @@ -183,6 +183,7 @@ OPTIONS
>         When merging notes, resolve notes conflicts using the given
>         strategy. The following strategies are recognized: "manual"
>         (default), "ours", "theirs", "union" and "cat_sort_uniq".
> +       This option overrides the "notes.mergestrategy" configuration setting.
>         See the "NOTES MERGE STRATEGIES" section below for more
>         information on each notes merge strategy.
>
> @@ -247,6 +248,9 @@ When done, the user can either finalize the merge with
>  'git notes merge --commit', or abort the merge with
>  'git notes merge --abort'.
>
> +Users may select an automated merge strategy from among the following using
> +either -s/--strategy option or configuring notes.mergestrategy accordingly:
> +
>  "ours" automatically resolves conflicting notes in favor of the local
>  version (i.e. the current notes ref).
>
> @@ -310,6 +314,14 @@ core.notesRef::
>         This setting can be overridden through the environment and
>         command line.
>
> +notes.mergestrategy::
Same here.
Show 10 quoted lines
> +       Which merge strategy to choose by default when resolving notes
> +       conflicts. Must be one of `manual`, `ours`, `theirs`, `union`,
> +       or `cat_sort_uniq`. Defaults to `manual`. See "NOTES MERGE
> +       STRATEGIES" section above for more information on each strategy.
> ++
> +This setting can be overridden by passing the `--strategy` option.
> +
>  notes.displayRef::
>         Which ref (or refs, if a glob or specified more than once), in
>         addition to the default set by `core.notesRef` or
[...]
Otherwise, looks good to me.
...Johan
-- 
Johan Herland, <johan@herland.net>
www.herland.net
Jacob Keller· Aug 14, 2015, 21:13 UTC · re: Jacob Keller · lore

[PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

From: Jacob Keller <jacob.keller@gmail.com>

Add new option "notes.<ref>.mergestrategy" option which specifies the merge strategy for merging into a given notes ref. This option enables selection of merge strategy for particular notes refs, rather than all notes ref merges, as user may not want cat_sort_uniq for all refs, but only some. Note that the <ref> is the local reference we are merging into, not the remote ref we merged from. The assumption is that users will mostly want to configure separate local ref merge strategies rather than strategies depending on which remote ref they merge from. Also, notes.<ref>.merge overrides the general behavior as it is more specific.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
 Documentation/config.txt            |  7 +++++++
 Documentation/git-notes.txt         |  6 ++++++
 builtin/notes.c                     | 13 ++++++++++---
 t/t3309-notes-merge-auto-resolve.sh | 39 +++++++++++++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 3 deletions(-)
Show changes to 4 files +62 −3

Documentation/config.txt, Documentation/git-notes.txt, builtin/notes.c, t/t3309-notes-merge-auto-resolve.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5e3e03459de7..47478311367e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1926,6 +1926,13 @@ notes.mergestrategy::
 	STRATEGIES" section of linkgit:git-notes[1] for more information
 	on each strategy.
 
+notes.<localref>.mergestrategy::
+	Which merge strategy to choose if the local ref for a notes merge
+	matches <localref>, overriding "notes.mergestrategy". <localref> must
+	be the short name of a ref under refs/notes/. See "NOTES MERGE
+	STRATEGIES" section in linkgit:git-notes[1] for more information on the
+	available strategies.
+
 notes.displayRef::
 	The (fully qualified) refname from which to show notes when
 	showing commit messages.  The value of this variable can be set
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 89c8829a0543..b99809fc81a6 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -322,6 +322,12 @@ notes.mergestrategy::
 +
 This setting can be overridden by passing the `--strategy` option.
 
+notes.<localref>.mergestrategy::
+	Which strategy to choose when merging into <localref>. The set of
+	allowed values is the same as "notes.mergestrategy". <localref> must be
+	the short name of a ref under refs/notes/. See "NOTES MERGE STRATEGIES"
+	section above for more information about each strategy.
+
 notes.displayRef::
 	Which ref (or refs, if a glob or specified more than once), in
 	addition to the default set by `core.notesRef` or
diff --git a/builtin/notes.c b/builtin/notes.c
index 12a42b583f98..bdfd9c7d29b4 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -771,13 +771,13 @@ static int git_config_get_notes_strategy(const char *key,
 
 static int merge(int argc, const char **argv, const char *prefix)
 {
-	struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
+	struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT, merge_key = STRBUF_INIT;
 	unsigned char result_sha1[20];
 	struct notes_tree *t;
 	struct notes_merge_options o;
 	int do_merge = 0, do_commit = 0, do_abort = 0;
 	int verbosity = 0, result;
-	const char *strategy = NULL;
+	const char *strategy = NULL, *short_ref = NULL;
 	struct option options[] = {
 		OPT_GROUP(N_("General options")),
 		OPT__VERBOSITY(&verbosity),
@@ -833,7 +833,14 @@ static int merge(int argc, const char **argv, const char *prefix)
 			usage_with_options(git_notes_merge_usage, options);
 		}
 	} else {
-		git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
+		if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
+			die("Refusing to merge notes into %s (outside of refs/notes/)",
+			    o.local_ref);
+
+		strbuf_addf(&merge_key, "notes.%s.mergestrategy", short_ref);
+
+		if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
+			git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
 	}
 
 	t = init_notes_check("merge");
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 476b9f5306f1..560e75259798 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -383,6 +383,17 @@ test_expect_success 'reset to pre-merge state (y)' '
 	verify_notes y y
 '
 
+test_expect_success 'merge z into y with "ours" per-ref configuration option => Non-conflicting 3-way merge' '
+	git -c notes.y.mergestrategy="ours" notes merge z &&
+	verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
 cat <<EOF | sort >expect_notes_theirs
 9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
@@ -534,6 +545,34 @@ test_expect_success 'reset to pre-merge state (y)' '
 	verify_notes y y
 '
 
+test_expect_success 'merge z into y with "union" strategy overriding per-ref configuration => Non-conflicting 3-way merge' '
+	git -c notes.y.mergestrategy="theirs" notes merge --strategy=union z &&
+	verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+test_expect_success 'merge z into y with "union" per-ref overriding general configuration => Non-conflicting 3-way merge' '
+	git -c notes.y.mergestrategy="union" -c notes.mergestrategy="theirs" notes merge z &&
+	verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+test_expect_success 'merge z into y with "manual" per-ref only checks specific ref configuration => Conflicting 3-way merge' '
+	test_must_fail git -c notes.z.mergestrategy="union" notes merge z &&
+	git notes merge --abort &&
+	verify_notes y y
+'
+
 cat <<EOF | sort >expect_notes_union2
 d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
-- 
2.5.0.280.g4aaba03
Junio C Hamano· Aug 14, 2015, 22:01 UTC · re: Jacob Keller · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

Jacob Keller <jacob.e.keller@intel.com> writes:
Show 14 quoted lines
> diff --git a/builtin/notes.c b/builtin/notes.c
> index 12a42b583f98..bdfd9c7d29b4 100644
> --- a/builtin/notes.c
> +++ b/builtin/notes.c
> ...
> @@ -833,7 +833,14 @@ static int merge(int argc, const char **argv, const char *prefix)
>  			usage_with_options(git_notes_merge_usage, options);
>  		}
>  	} else {
> -		git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
> +		if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
> +			die("Refusing to merge notes into %s (outside of refs/notes/)",
> +			    o.local_ref);
> +
Sorry, but I lost track.  

Do I understand correctly the consensus on the previous discussion? My understanding is:

 (1) We do not currently refuse to merge notes into anywhere outside
     of refs/notes/;
 (2) But that is not a designed behaviour---we simply forgot to
     check it---we should start checking and refusing.

If that is the concensus, having this check somewhere in the merge() function is indeed necessary, but this looks very out of place. Think what happens if the user passes "--stratagy manual" from the command line. This check is not even performed, is it?

I'd prefer to see:
 * "Let's start making sure that we do not allow touching outside
   refs/notes/" as a separate patch, perhaps as a preparatory step.
 * Have the check apply consistently, regardless of where the
   strategy comes from.
 * That separate patch to add this restriction should test that
   the refusal triggers correctly when it should, and it does not
   trigger when it shouldn't.
Show 5 quoted lines
> +		strbuf_addf(&merge_key, "notes.%s.mergestrategy", short_ref);
> +
> +		if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
> +			git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>  	}
I think you are leaking merge_key after you are done using it.
It is tempting to suggest writing the above like so:
		git_config_get_notes_strategy(merge_key.buf, &o.strategy)) ||
                git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);

which might make it more obvious what is going on, but I do not care too deeply about it. To be honest, I am not sure which one is easier to read in the longer term myself ;-).

Thanks.
Eric Sunshine· Aug 14, 2015, 22:10 UTC · re: Junio C Hamano · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

On Fri, Aug 14, 2015 at 6:01 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 12 quoted lines
> Jacob Keller <jacob.e.keller@intel.com> writes:
>> diff --git a/builtin/notes.c b/builtin/notes.c
>> index 12a42b583f98..bdfd9c7d29b4 100644
>> --- a/builtin/notes.c
>> +++ b/builtin/notes.c
>> +             strbuf_addf(&merge_key, "notes.%s.mergestrategy", short_ref);
>> +
>> +             if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
>> +                     git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>>       }
>
> I think you are leaking merge_key after you are done using it.

In addition to fixing the leak, since 'merge_key' is only used within this block, it might also make sense to declare it in this block rather than at the top of the function.

Jacob Keller· Aug 14, 2015, 22:50 UTC · re: Eric Sunshine · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

On Fri, Aug 14, 2015 at 3:10 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
Show 17 quoted lines
> On Fri, Aug 14, 2015 at 6:01 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Jacob Keller <jacob.e.keller@intel.com> writes:
>>> diff --git a/builtin/notes.c b/builtin/notes.c
>>> index 12a42b583f98..bdfd9c7d29b4 100644
>>> --- a/builtin/notes.c
>>> +++ b/builtin/notes.c
>>> +             strbuf_addf(&merge_key, "notes.%s.mergestrategy", short_ref);
>>> +
>>> +             if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
>>> +                     git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>>>       }
>>
>> I think you are leaking merge_key after you are done using it.
>
> In addition to fixing the leak, since 'merge_key' is only used within
> this block, it might also make sense to declare it in this block
> rather than at the top of the function.
I can do that.

How do you feel about having the duplicate check for the short_ref? We *already* check this inside init_notes_check() which is called right after this.

I think that we should keep it but can't find a consistent way to avoid the duplication.

In addition, we already provide the tests for merging into and from non-notes refs.

Regards, Jake

Junio C Hamano· Aug 17, 2015, 17:33 UTC · re: Jacob Keller · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

Jacob Keller <jacob.keller@gmail.com> writes:
> How do you feel about having the duplicate check for the short_ref? We
> *already* check this inside init_notes_check() which is called right
> after this.

I thought you were trying to enforce a new rule (i.e. must be under "refs/notes/") with this, but it isn't. It is "we are going to strip the prefix known to us, just make sure the caller did not feed us something bogus" safety, and the placement of this new check in your patch (i.e. only when strategy was not given so we need to check which short-ref we are dealing with) is the best place.

Thanks.
Jacob Keller· Aug 14, 2015, 22:48 UTC · re: Junio C Hamano · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

On Fri, Aug 14, 2015 at 3:01 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 25 quoted lines
> Jacob Keller <jacob.e.keller@intel.com> writes:
>
>> diff --git a/builtin/notes.c b/builtin/notes.c
>> index 12a42b583f98..bdfd9c7d29b4 100644
>> --- a/builtin/notes.c
>> +++ b/builtin/notes.c
>> ...
>> @@ -833,7 +833,14 @@ static int merge(int argc, const char **argv, const char *prefix)
>>                       usage_with_options(git_notes_merge_usage, options);
>>               }
>>       } else {
>> -             git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>> +             if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
>> +                     die("Refusing to merge notes into %s (outside of refs/notes/)",
>> +                         o.local_ref);
>> +
>
> Sorry, but I lost track.
>
> Do I understand correctly the consensus on the previous discussion?
> My understanding is:
>
>  (1) We do not currently refuse to merge notes into anywhere outside
>      of refs/notes/;
>

We do. I mis understood the original code. We check inside "init_notes_check()", which will check if the ref is under refs/notes/

Show 8 quoted lines
>  (2) But that is not a designed behaviour---we simply forgot to
>      check it---we should start checking and refusing.
>
> If that is the concensus, having this check somewhere in the merge()
> function is indeed necessary, but this looks very out of place.
> Think what happens if the user passes "--stratagy manual" from the
> command line.  This check is not even performed, is it?
>

It is checked (also) in init_notes_check(). I just happen to re-check here because I didn't want to out-right ignore it in some weird flow where it was incorrect.

Show 5 quoted lines
> I'd prefer to see:
>
>  * "Let's start making sure that we do not allow touching outside
>    refs/notes/" as a separate patch, perhaps as a preparatory step.
>
We already don't allow it. See init_notes_check()
>  * Have the check apply consistently, regardless of where the
>    strategy comes from.

It already does. There is just a second check. I could completely remove that check if you like, but then we'd check config since we don't run init_notes_check until after we find the merge strategy.

Show 22 quoted lines
>
>  * That separate patch to add this restriction should test that
>    the refusal triggers correctly when it should, and it does not
>    trigger when it shouldn't.
>
>> +             strbuf_addf(&merge_key, "notes.%s.mergestrategy", short_ref);
>> +
>> +             if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
>> +                     git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>>       }
>
> I think you are leaking merge_key after you are done using it.
>
> It is tempting to suggest writing the above like so:
>
>                 git_config_get_notes_strategy(merge_key.buf, &o.strategy)) ||
>                 git_config_get_notes_strategy("notes.mergestrategy", &o.strategy);
>
> which might make it more obvious what is going on, but I do not care
> too deeply about it.  To be honest, I am not sure which one is
> easier to read in the longer term myself ;-).
>
 the || strategy results in a warning that we are checking and then
dropping the outcome.
> Thanks.

Regards, Jake

Junio C Hamano· Aug 17, 2015, 17:22 UTC · re: Jacob Keller · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

Jacob Keller <jacob.keller@gmail.com> writes:
Show 10 quoted lines
>> Sorry, but I lost track.
>>
>> Do I understand correctly the consensus on the previous discussion?
>> My understanding is:
>>
>>  (1) We do not currently refuse to merge notes into anywhere outside
>>      of refs/notes/;
>
> We do. I mis understood the original code. We check inside
> "init_notes_check()", which will check if the ref is under refs/notes/
OK, then we are in a much better shape than I thought ;-).  Thanks.
Johan Herland· Aug 15, 2015, 09:25 UTC · re: Jacob Keller · lore

Re: [PATCH v7 4/4] notes: teach git-notes about notes.<ref>.mergestrategy option

On Fri, Aug 14, 2015 at 11:13 PM, Jacob Keller <jacob.e.keller@intel.com> wrote:
Show 29 quoted lines
> From: Jacob Keller <jacob.keller@gmail.com>
>
> Add new option "notes.<ref>.mergestrategy" option which specifies the merge
> strategy for merging into a given notes ref. This option enables
> selection of merge strategy for particular notes refs, rather than all
> notes ref merges, as user may not want cat_sort_uniq for all refs, but
> only some. Note that the <ref> is the local reference we are merging
> into, not the remote ref we merged from. The assumption is that users
> will mostly want to configure separate local ref merge strategies rather
> than strategies depending on which remote ref they merge from. Also,
> notes.<ref>.merge overrides the general behavior as it is more specific.
>
> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> ---
>  Documentation/config.txt            |  7 +++++++
>  Documentation/git-notes.txt         |  6 ++++++
>  builtin/notes.c                     | 13 ++++++++++---
>  t/t3309-notes-merge-auto-resolve.sh | 39 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 62 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 5e3e03459de7..47478311367e 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -1926,6 +1926,13 @@ notes.mergestrategy::
>         STRATEGIES" section of linkgit:git-notes[1] for more information
>         on each strategy.
>
> +notes.<localref>.mergestrategy::
Nit: mergeStrategy
> +       Which merge strategy to choose if the local ref for a notes merge
> +       matches <localref>, overriding "notes.mergestrategy". <localref> must
> +       be the short name of a ref under refs/notes/. See "NOTES MERGE
An example would be useful here, methinks:

<localref> must be the short name of a ref under refs/notes/, e.g. for configuring the merge strategy for refs/notes/commits, notes.commits.mergeStrategy must be set.

Otherwise, the patch looks good to me.
...Johan
-- 
Johan Herland, <johan@herland.net>
www.herland.net

← back to recent threads