{"thread":{"id":"39985","subject":"[PATCH 1/2] notes: document cat_sort_uniq rewriteMode","startedAt":"2015-07-31T19:21:34Z","lastAt":"2015-07-31T21:12:53Z","messageCount":5,"participants":["Jacob Keller","Junio C Hamano","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"267219","messageId":"1438370496-26433-1-git-send-email-jacob.e.keller@intel.com","threadId":"39985","inReplyTo":null,"subject":"[PATCH 0/2] add notes.merge configuration variable","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2015-07-31T19:21:34Z","receivedAt":"2015-07-31T19:21:34Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nThis small series is a precursor to some thoughts I have for enabling\neasier notes collaboration. First, make it so that users can configure\nthe default notes merge strategy. Mostly useful if you always want to\nuse cat_sort_uniq.\n\nI also found that the rewriteMode supports cat_sort_uniq but does not\ndocument this fact.\n\nJacob Keller (2):\n  notes: document cat_sort_uniq rewriteMode\n  notes: add notes.merge option to select default strategy\n\n Documentation/config.txt              | 11 ++++-\n Documentation/git-notes.txt           | 33 +++++++++------\n builtin/notes.c                       | 75 +++++++++++++++++++++++++----------\n notes-merge.h                         | 16 ++++----\n t/t3309-notes-merge-auto-resolve.sh   | 29 ++++++++++++++\n t/t3310-notes-merge-manual-resolve.sh | 12 ++++++\n 6 files changed, 133 insertions(+), 43 deletions(-)\n\n-- \n2.5.0.482.gfcd5645\n"},{"id":"267218","messageId":"1438370496-26433-2-git-send-email-jacob.e.keller@intel.com","threadId":"39985","inReplyTo":"1438370496-26433-1-git-send-email-jacob.e.keller@intel.com","subject":"[PATCH 1/2] notes: document cat_sort_uniq rewriteMode","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2015-07-31T19:21:35Z","receivedAt":"2015-07-31T19:21:35Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nTeach documentation about the cat_sort_uniq rewriteMode that got added\nat the same time as the equivalent merge strategy.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/config.txt    | 4 ++--\n Documentation/git-notes.txt | 3 ++-\n 2 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6562c6ab09b9..3c1e4df09beb 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1965,8 +1965,8 @@ notes.rewriteMode::\n \tWhen copying notes during a rewrite (see the\n \t\"notes.rewrite.<command>\" option), determines what to do if\n \tthe target commit already has a note.  Must be one of\n-\t`overwrite`, `concatenate`, or `ignore`.  Defaults to\n-\t`concatenate`.\n+\t`overwrite`, `concatenate`, `cat_sort_uniq`, or `ignore`.\n+\tDefaults to `concatenate`.\n +\n This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`\n environment variable.\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 851518d531b5..674682b34b83 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -331,7 +331,8 @@ environment variable.\n notes.rewriteMode::\n \tWhen copying notes during a rewrite, what to do if the target\n \tcommit already has a note.  Must be one of `overwrite`,\n-\t`concatenate`, and `ignore`.  Defaults to `concatenate`.\n+\t`concatenate`, `cat_sort_uniq`, or `ignore`.  Defaults to\n+\t`concatenate`.\n +\n This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`\n environment variable.\n-- \n2.5.0.482.gfcd5645\n"},{"id":"267220","messageId":"1438370496-26433-3-git-send-email-jacob.e.keller@intel.com","threadId":"39985","inReplyTo":"1438370496-26433-1-git-send-email-jacob.e.keller@intel.com","subject":"[PATCH 2/2] notes: add notes.merge option to select default strategy","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2015-07-31T19:21:36Z","receivedAt":"2015-07-31T19:21:36Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nTeach git-notes about a new configuration option \"notes.merge\" for\nselecting the default notes merge strategy. Document the option in\nconfig.txt and git-notes.txt\n\nAdd tests for the configuration option. Ensure that command line\n--strategy option overrides the configured value. Ensure that -s can't\nbe passed with --commit or --abort. Ensure that --abort and --commit\ncan't be used together.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/config.txt              |  7 ++++\n Documentation/git-notes.txt           | 30 +++++++++-----\n builtin/notes.c                       | 75 +++++++++++++++++++++++++----------\n notes-merge.h                         | 16 ++++----\n t/t3309-notes-merge-auto-resolve.sh   | 29 ++++++++++++++\n t/t3310-notes-merge-manual-resolve.sh | 12 ++++++\n 6 files changed, 129 insertions(+), 40 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3c1e4df09beb..85c15126e4ce 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1937,6 +1937,13 @@ mergetool.writeToTemp::\n mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\n \n+notes.merge::\n+\tWhich merge strategy to choose by default when resolving notes\n+\tconflicts. Must be one of `manual`, `ours`, `theirs`, `union`,\n+\tor `cat_sort_uniq`. Defaults to `manual`. See \"NOTES MERGE\n+\tSTRATEGIES\" section of linkgit:git-notes[1] for more information\n+\ton each strategy.\n+\n notes.displayRef::\n \tThe (fully qualified) refname from which to show notes when\n \tshowing commit messages.  The value of this variable can be set\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 674682b34b83..d8944f5aec60 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -101,13 +101,13 @@ merge::\n \tany) into the current notes ref (called \"local\").\n +\n If conflicts arise and a strategy for automatically resolving\n-conflicting notes (see the -s/--strategy option) is not given,\n-the \"manual\" resolver is used. This resolver checks out the\n-conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),\n-and instructs the user to manually resolve the conflicts there.\n-When done, the user can either finalize the merge with\n-'git notes merge --commit', or abort the merge with\n-'git notes merge --abort'.\n+conflicting notes (see the -s/--strategy option or notes.merge\n+config option) is not given, the \"manual\" resolver is used.\n+This resolver checks out the conflicting notes in a special\n+worktree (`.git/NOTES_MERGE_WORKTREE`), and instructs the user\n+to manually resolve the conflicts there. When done, the user\n+can either finalize the merge with 'git notes merge --commit',\n+or abort the merge with 'git notes merge --abort'.\n \n remove::\n \tRemove the notes for given objects (defaults to HEAD). When\n@@ -181,10 +181,10 @@ OPTIONS\n -s <strategy>::\n --strategy=<strategy>::\n \tWhen merging notes, resolve notes conflicts using the given\n-\tstrategy. The following strategies are recognized: \"manual\"\n-\t(default), \"ours\", \"theirs\", \"union\" and \"cat_sort_uniq\".\n-\tSee the \"NOTES MERGE STRATEGIES\" section below for more\n-\tinformation on each notes merge strategy.\n+\tstrategy. Overrides \"notes.merge\". The following strategies are\n+\trecognized: `manual`, `ours`, `theirs`, `union` and\n+\t`cat_sort_uniq`. See the `NOTES MERGE STRATEGIES` section below\n+\tfor more information on each notes merge strategy.\n \n --commit::\n \tFinalize an in-progress 'git notes merge'. Use this option\n@@ -310,6 +310,14 @@ core.notesRef::\n \tThis setting can be overridden through the environment and\n \tcommand line.\n \n+notes.merge::\n+\tWhich merge strategy to choose by default when resolving notes\n+\tconflicts. Must be one of `manual`, `ours`, `theirs`, `union`,\n+\tor `cat_sort_uniq`. Defaults to `manual`. See \"NOTES MERGE\n+\tSTRATEGIES\" section above for more information on each strategy.\n++\n+This setting can be overridden by passing the `--strategy` option.\n+\n notes.displayRef::\n \tWhich ref (or refs, if a glob or specified more than once), in\n \taddition to the default set by `core.notesRef` or\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 88fdafb32bc0..7123311b563e 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -93,6 +93,9 @@ static const char * const git_notes_get_ref_usage[] = {\n static const char note_template[] =\n \t\"\\nWrite/edit the notes for the following object:\\n\";\n \n+static int override_strategy = 0;\n+static enum notes_merge_strategy strategy;\n+\n struct note_data {\n \tint given;\n \tint use_editor;\n@@ -741,6 +744,36 @@ static int merge_commit(struct notes_merge_options *o)\n \treturn ret;\n }\n \n+static int parse_notes_strategy(const char *var, const char *arg,\n+\t\t\t\tenum notes_merge_strategy *strategy)\n+{\n+\tif (!strcmp(arg, \"manual\"))\n+\t\t*strategy = NOTES_MERGE_RESOLVE_MANUAL;\n+\telse if (!strcmp(arg, \"ours\"))\n+\t\t*strategy = NOTES_MERGE_RESOLVE_OURS;\n+\telse if (!strcmp(arg, \"theirs\"))\n+\t\t*strategy = NOTES_MERGE_RESOLVE_THEIRS;\n+\telse if (!strcmp(arg, \"union\"))\n+\t\t*strategy = NOTES_MERGE_RESOLVE_UNION;\n+\telse if (!strcmp(arg, \"cat_sort_uniq\"))\n+\t\t*strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;\n+\telse {\n+\t\treturn error(\"Unknown notes merge strategy: %s\", arg);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int parse_opt_strategy(const struct option *opt, const char *arg, int unset)\n+{\n+\tenum notes_merge_strategy *strategy = opt->value;\n+\n+\t/* let merge() know we overrode the configured strategy */\n+\toverride_strategy = 1;\n+\n+\treturn parse_notes_strategy(NULL, arg, strategy);\n+}\n+\n static int merge(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;\n@@ -749,14 +782,16 @@ static int merge(int argc, const char **argv, const char *prefix)\n \tstruct notes_merge_options o;\n \tint do_merge = 0, do_commit = 0, do_abort = 0;\n \tint verbosity = 0, result;\n-\tconst char *strategy = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(N_(\"General options\")),\n \t\tOPT__VERBOSITY(&verbosity),\n \t\tOPT_GROUP(N_(\"Merge options\")),\n-\t\tOPT_STRING('s', \"strategy\", &strategy, N_(\"strategy\"),\n-\t\t\t   N_(\"resolve notes conflicts using the given strategy \"\n-\t\t\t      \"(manual/ours/theirs/union/cat_sort_uniq)\")),\n+\t\t{\n+\t\t\tOPTION_CALLBACK, 's', \"strategy\", &strategy, N_(\"strategy\"),\n+\t\t\tN_(\"resolve notes conflicts using the given strategy \"\n+\t\t\t   \"(manual/ours/theirs/union/cat_sort_uniq)\"),\n+\t\t\tPARSE_OPT_NONEG, parse_opt_strategy\n+\t\t},\n \t\tOPT_GROUP(N_(\"Committing unmerged notes\")),\n \t\t{ OPTION_SET_INT, 0, \"commit\", &do_commit, NULL,\n \t\t\tN_(\"finalize notes merge by committing unmerged notes\"),\n@@ -771,7 +806,7 @@ static int merge(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     git_notes_merge_usage, 0);\n \n-\tif (strategy || do_commit + do_abort == 0)\n+\tif (override_strategy || do_commit + do_abort == 0)\n \t\tdo_merge = 1;\n \tif (do_merge + do_commit + do_abort != 1) {\n \t\terror(\"cannot mix --commit, --abort or -s/--strategy\");\n@@ -799,22 +834,7 @@ static int merge(int argc, const char **argv, const char *prefix)\n \texpand_notes_ref(&remote_ref);\n \to.remote_ref = remote_ref.buf;\n \n-\tif (strategy) {\n-\t\tif (!strcmp(strategy, \"manual\"))\n-\t\t\to.strategy = NOTES_MERGE_RESOLVE_MANUAL;\n-\t\telse if (!strcmp(strategy, \"ours\"))\n-\t\t\to.strategy = NOTES_MERGE_RESOLVE_OURS;\n-\t\telse if (!strcmp(strategy, \"theirs\"))\n-\t\t\to.strategy = NOTES_MERGE_RESOLVE_THEIRS;\n-\t\telse if (!strcmp(strategy, \"union\"))\n-\t\t\to.strategy = NOTES_MERGE_RESOLVE_UNION;\n-\t\telse if (!strcmp(strategy, \"cat_sort_uniq\"))\n-\t\t\to.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;\n-\t\telse {\n-\t\t\terror(\"Unknown -s/--strategy: %s\", strategy);\n-\t\t\tusage_with_options(git_notes_merge_usage, options);\n-\t\t}\n-\t}\n+\to.strategy = strategy;\n \n \tt = init_notes_check(\"merge\", NOTES_INIT_WRITABLE);\n \n@@ -950,6 +970,17 @@ static int get_ref(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static int git_notes_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"notes.merge\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\treturn parse_notes_strategy(var, value, &strategy);\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n int cmd_notes(int argc, const char **argv, const char *prefix)\n {\n \tint result;\n@@ -960,7 +991,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_notes_config, NULL);\n \targc = parse_options(argc, argv, prefix, options, git_notes_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \ndiff --git a/notes-merge.h b/notes-merge.h\nindex 1d01f6aacf54..bda8c0c8d348 100644\n--- a/notes-merge.h\n+++ b/notes-merge.h\n@@ -8,18 +8,20 @@ enum notes_merge_verbosity {\n \tNOTES_MERGE_VERBOSITY_MAX = 5\n };\n \n+enum notes_merge_strategy {\n+\tNOTES_MERGE_RESOLVE_MANUAL = 0,\n+\tNOTES_MERGE_RESOLVE_OURS,\n+\tNOTES_MERGE_RESOLVE_THEIRS,\n+\tNOTES_MERGE_RESOLVE_UNION,\n+\tNOTES_MERGE_RESOLVE_CAT_SORT_UNIQ\n+};\n+\n struct notes_merge_options {\n \tconst char *local_ref;\n \tconst char *remote_ref;\n \tstruct strbuf commit_msg;\n \tint verbosity;\n-\tenum {\n-\t\tNOTES_MERGE_RESOLVE_MANUAL = 0,\n-\t\tNOTES_MERGE_RESOLVE_OURS,\n-\t\tNOTES_MERGE_RESOLVE_THEIRS,\n-\t\tNOTES_MERGE_RESOLVE_UNION,\n-\t\tNOTES_MERGE_RESOLVE_CAT_SORT_UNIQ\n-\t} strategy;\n+\tenum notes_merge_strategy strategy;\n \tunsigned has_worktree:1;\n };\n \ndiff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh\nindex 461fd84755d7..a773b01b73db 100755\n--- a/t/t3309-notes-merge-auto-resolve.sh\n+++ b/t/t3309-notes-merge-auto-resolve.sh\n@@ -298,6 +298,13 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '\n \tverify_notes y y\n '\n \n+test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '\n+\tgit config core.notesRef refs/notes/y &&\n+\ttest_must_fail git -c notes.merge=\"foo\" notes merge z &&\n+\t# Verify no changes (y)\n+\tverify_notes y y\n+'\n+\n cat <<EOF | sort >expect_notes_ours\n 68b8630d25516028bed862719855b3d6768d7833 $commit_sha15\n 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14\n@@ -365,6 +372,17 @@ test_expect_success 'reset to pre-merge state (y)' '\n \tverify_notes y y\n '\n \n+test_expect_success 'merge z into y with \"ours\" configuration option => Non-conflicting 3-way merge' '\n+\tgit -c notes.merge=\"ours\" notes merge z &&\n+\tverify_notes y ours\n+'\n+\n+test_expect_success 'reset to pre-merge state (y)' '\n+\tgit update-ref refs/notes/y refs/notes/y^1 &&\n+\t# Verify pre-merge state\n+\tverify_notes y y\n+'\n+\n cat <<EOF | sort >expect_notes_theirs\n 9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15\n 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14\n@@ -432,6 +450,17 @@ test_expect_success 'reset to pre-merge state (y)' '\n \tverify_notes y y\n '\n \n+test_expect_success 'merge z into y with \"theirs\" strategy overriding configuration option \"ours\" => Non-conflicting 3-way merge' '\n+\tgit -c notes.merge=\"ours\" notes merge --strategy=theirs z &&\n+\tverify_notes y theirs\n+'\n+\n+test_expect_success 'reset to pre-merge state (y)' '\n+\tgit update-ref refs/notes/y refs/notes/y^1 &&\n+\t# Verify pre-merge state\n+\tverify_notes y y\n+'\n+\n cat <<EOF | sort >expect_notes_union\n 7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15\n 5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14\ndiff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh\nindex 195bb97f859d..d5572121da69 100755\n--- a/t/t3310-notes-merge-manual-resolve.sh\n+++ b/t/t3310-notes-merge-manual-resolve.sh\n@@ -314,6 +314,18 @@ y and z notes on 1st commit\n \n EOF\n \n+test_expect_success 'do not allow mixing --commit and --abort' '\n+\ttest_must_fail git notes merge --commit --abort\n+'\n+\n+test_expect_success 'do not allow mixing --commit and --strategy' '\n+\ttest_must_fail git notes merge --commit --strategy theirs\n+'\n+\n+test_expect_success 'do not allow mixing --abort and --strategy' '\n+\ttest_must_fail git notes merge --abort --strategy theirs\n+'\n+\n test_expect_success 'finalize conflicting merge (z => m)' '\n \t# Resolve conflicts and finalize merge\n \tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&\n-- \n2.5.0.482.gfcd5645\n"},{"id":"267222","messageId":"xmqqvbd03xo5.fsf@gitster.dls.corp.google.com","threadId":"39985","inReplyTo":"1438370496-26433-1-git-send-email-jacob.e.keller@intel.com","subject":"Re: [PATCH 0/2] add notes.merge configuration variable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-31T20:00:10Z","receivedAt":"2015-07-31T20:00:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"CC'ing yourself is ok, but please do not forget to ask reviews from\npeople who have touched the area of the code you are touching (they\nare hopefully the ones who are more knowledgeable and can help you).\n\nOld timers like I know Johan is the go-to person on things around\nnotes, but new people can ask their copy of Git:\n\n$ git shortlog -n -s --no-merges master builtin/notes.c notes.c | head -n 5\n    46\tJohan Herland\n    11\tJunio C Hamano\n    11\tMichael Haggerty\n     5\tJeff King\n     5\tNguyễn Thái Ngọc Duy\n\nThe changes to the code looked reasonable from a cursory read,\nthough.\n\nThanks.\n"},{"id":"267225","messageId":"CAPig+cT+-d25NeXaNXP6rk8WPEtHJB7dobme72aLF1JL8y9mVg@mail.gmail.com","threadId":"39985","inReplyTo":"1438370496-26433-3-git-send-email-jacob.e.keller@intel.com","subject":"Re: [PATCH 2/2] notes: add notes.merge option to select default strategy","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-31T21:12:53Z","receivedAt":"2015-07-31T21:12:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jul 31, 2015 at 3:21 PM, Jacob Keller <jacob.e.keller@intel.com> wrote:\n> Teach git-notes about a new configuration option \"notes.merge\" for\n> selecting the default notes merge strategy. Document the option in\n> config.txt and git-notes.txt\n>\n> Add tests for the configuration option. Ensure that command line\n> --strategy option overrides the configured value. Ensure that -s can't\n> be passed with --commit or --abort. Ensure that --abort and --commit\n> can't be used together.\n>\n> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>\n> ---\n> diff --git a/builtin/notes.c b/builtin/notes.c\n> index 88fdafb32bc0..7123311b563e 100644\n> --- a/builtin/notes.c\n> +++ b/builtin/notes.c\n> @@ -741,6 +744,36 @@ static int merge_commit(struct notes_merge_options *o)\n> +static int parse_notes_strategy(const char *var, const char *arg,\n> +                               enum notes_merge_strategy *strategy)\n\nWhat is the 'var' argument for? It's not used by the function.\n\n> +{\n> +       if (!strcmp(arg, \"manual\"))\n> +               *strategy = NOTES_MERGE_RESOLVE_MANUAL;\n> +       else if (!strcmp(arg, \"ours\"))\n> +               *strategy = NOTES_MERGE_RESOLVE_OURS;\n> +       else if (!strcmp(arg, \"theirs\"))\n> +               *strategy = NOTES_MERGE_RESOLVE_THEIRS;\n> +       else if (!strcmp(arg, \"union\"))\n> +               *strategy = NOTES_MERGE_RESOLVE_UNION;\n> +       else if (!strcmp(arg, \"cat_sort_uniq\"))\n> +               *strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;\n> +       else {\n> +               return error(\"Unknown notes merge strategy: %s\", arg);\n\nPerhaps reporting of the error should be left to the callers so they\ncan tailor the message (\"--strategy\" vs. \"notes.merge\")? Doing so\nwould make it easier for the user to trace the source of a bogus\nvalue.\n\n> +       }\n> +\n> +       return 0;\n> +}\n> +\n> +static int parse_opt_strategy(const struct option *opt, const char *arg, int unset)\n> +{\n> +       enum notes_merge_strategy *strategy = opt->value;\n> +\n> +       /* let merge() know we overrode the configured strategy */\n> +       override_strategy = 1;\n> +\n> +       return parse_notes_strategy(NULL, arg, strategy);\n> +}\n> +\n>  static int merge(int argc, const char **argv, const char *prefix)\n>  {\n>         struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;\n> @@ -749,14 +782,16 @@ static int merge(int argc, const char **argv, const char *prefix)\n> -       const char *strategy = NULL;\n>         struct option options[] = {\n>                 OPT_GROUP(N_(\"General options\")),\n>                 OPT__VERBOSITY(&verbosity),\n>                 OPT_GROUP(N_(\"Merge options\")),\n> -               OPT_STRING('s', \"strategy\", &strategy, N_(\"strategy\"),\n> -                          N_(\"resolve notes conflicts using the given strategy \"\n> -                             \"(manual/ours/theirs/union/cat_sort_uniq)\")),\n> +               {\n> +                       OPTION_CALLBACK, 's', \"strategy\", &strategy, N_(\"strategy\"),\n> +                       N_(\"resolve notes conflicts using the given strategy \"\n> +                          \"(manual/ours/theirs/union/cat_sort_uniq)\"),\n> +                       PARSE_OPT_NONEG, parse_opt_strategy\n> +               },\n\nWhy change this from an OPT_STRING to an OPTION_CALLBACK? Couldn't you\njust as easily have called parse_notes_strategy() after\nparse_options()?\n\n>                 OPT_GROUP(N_(\"Committing unmerged notes\")),\n>                 { OPTION_SET_INT, 0, \"commit\", &do_commit, NULL,\n>                         N_(\"finalize notes merge by committing unmerged notes\"),\n> @@ -771,7 +806,7 @@ static int merge(int argc, const char **argv, const char *prefix)\n>         argc = parse_options(argc, argv, prefix, options,\n>                              git_notes_merge_usage, 0);\n>\n> -       if (strategy || do_commit + do_abort == 0)\n> +       if (override_strategy || do_commit + do_abort == 0)\n>                 do_merge = 1;\n>         if (do_merge + do_commit + do_abort != 1) {\n>                 error(\"cannot mix --commit, --abort or -s/--strategy\");\n> @@ -799,22 +834,7 @@ static int merge(int argc, const char **argv, const char *prefix)\n>         expand_notes_ref(&remote_ref);\n>         o.remote_ref = remote_ref.buf;\n>\n> -       if (strategy) {\n> -               if (!strcmp(strategy, \"manual\"))\n> -                       o.strategy = NOTES_MERGE_RESOLVE_MANUAL;\n> -               else if (!strcmp(strategy, \"ours\"))\n> -                       o.strategy = NOTES_MERGE_RESOLVE_OURS;\n> -               else if (!strcmp(strategy, \"theirs\"))\n> -                       o.strategy = NOTES_MERGE_RESOLVE_THEIRS;\n> -               else if (!strcmp(strategy, \"union\"))\n> -                       o.strategy = NOTES_MERGE_RESOLVE_UNION;\n> -               else if (!strcmp(strategy, \"cat_sort_uniq\"))\n> -                       o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;\n> -               else {\n> -                       error(\"Unknown -s/--strategy: %s\", strategy);\n> -                       usage_with_options(git_notes_merge_usage, options);\n> -               }\n> -       }\n> +       o.strategy = strategy;\n"}]}