{"thread":{"id":"48322","subject":"[RFC WIP PATCH] merge: implement -s theirs -X N","startedAt":"2018-04-18T22:48:41Z","lastAt":"2018-04-20T10:15:02Z","messageCount":7,"participants":["Ævar Arnfjörð Bjarmason","Stefan Beller","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"345040","messageId":"87sh7sdtc1.fsf@evledraar.gmail.com","threadId":"48322","inReplyTo":null,"subject":"[RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-18T22:48:30Z","receivedAt":"2018-04-18T22:48:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We have a -s ours, but not a -s theirs. This is a WIP patch to implement\nthat. It works, but I haven't dealt with this part of the internal API\nbefore, comments most welcome.\n\nThe purpose of this is that I'm working with a rollout tool that is\ncapable of doing hotfixes on top of old commits on \"master\".\n\nIt does this by cherry-picking a commit from origin/master, and then\nmerges it with origin/master & pushes it back, before finally reset\n--hard to the cherry-pick & rolling out.\n\nThe reason it's doing this is to maintain the guarantee that all rolled\nout commits are reachable from \"master\", and to handle the more general\ncase where original work is made during a hotfix, we don't want to then\ndo a subsequent \"normal\" rollout and miss the fix.\n\nIn cases where I detect (by looking at patch-id's) that the only commits\nthat are being hotfixed are those cherry-picked from upstream, I want to\nfully resolve the merge in favor of @{u}, i.e. end up with the same tree\nSHA-1. This is the inverse of -s ours, but we have no -s theirs, this\nimplements that.\n\nThe `-s recursive -X theirs strategy` won't do, because that will just\nresolve conflicts in favor of @{u}, but will screw up the well-known\ncases where a merge produces bad results with no conflicts, due to edge\ncases where patches apply cleanly but produce broken programs.\n\nSo this can be used as `-s theirs -X 2 @{u}` to implement that. The `-s\nours` strategy is then equivalent ot `-s theirs -X 1` (1st commit), and\nwe can do any value of `-X N` for octopus merges.\n\nAs noted `-s theirs` should be the same as supplying it with `-X 2`, but\nI haven't implemented that yet.\n\nQuestions:\n\n 1. Should I be calling read-tree here with run_command_v_opt(), or is\n    there some internal API I should be using?\n\n 2. Currently merge-ours is just a no-op since we take the current HEAD,\n    but maybe it would be cleaner to implement it in terms of this\n    thing, also to get immediate test coverage for all the -s ours\n    stuff. We'd be reading the tree redundantly into the index, but\n    maybe it's worth it for the coverage...\n\n 3. Is there a better name for this than -s theirs? Maybe `-s nth -X N`?\n\ndiff --git a/.gitignore b/.gitignore\nindex 833ef3b0b7..65d62b191f 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -93,6 +93,7 @@\n /git-merge-recursive\n /git-merge-resolve\n /git-merge-subtree\n+/git-merge-theirs\n /git-mergetool\n /git-mergetool--lib\n /git-mktag\ndiff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt\nindex 4a58aad4b8..a84482aafc 100644\n--- a/Documentation/merge-strategies.txt\n+++ b/Documentation/merge-strategies.txt\n@@ -117,6 +117,12 @@ ours::\n \tbranches.  Note that this is different from the -Xours option to\n \tthe 'recursive' merge strategy.\n\n+theirs::\n+\tThis resolves any number of heads, but the resulting tree of\n+\tthe merge is always that of the Nth branch head specified via\n+\t`-X n`. If it's not specified it'll default ot `-X 2`,\n+\tsupplying `-X 1` makes this equivalent to the `ours` strategy.\n+\n subtree::\n \tThis is a modified recursive strategy. When merging trees A and\n \tB, if B corresponds to a subtree of A, B is first adjusted to\ndiff --git a/Makefile b/Makefile\nindex f181687250..00ded0c37c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -998,6 +998,7 @@ BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n+BUILTIN_OBJS += builtin/merge-theirs.o\n BUILTIN_OBJS += builtin/merge-tree.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n@@ -2815,6 +2816,7 @@ check-docs::\n \t\tcase \"$$v\" in \\\n \t\tgit-merge-octopus | git-merge-ours | git-merge-recursive | \\\n \t\tgit-merge-resolve | git-merge-subtree | \\\n+\t\tgit-merge-theirs \\\n \t\tgit-fsck-objects | git-init-db | \\\n \t\tgit-remote-* | git-stage | \\\n \t\tgit-?*--?* ) continue ;; \\\ndiff --git a/builtin.h b/builtin.h\nindex 42378f3aa4..a48eaf3a67 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -187,6 +187,7 @@ extern int cmd_merge_index(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_file(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);\n+extern int cmd_merge_theirs(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_mktag(int argc, const char **argv, const char *prefix);\n extern int cmd_mktree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/merge-ours.c b/builtin/merge-ours.c\nindex c84c6e05e9..da5ba0b370 100644\n--- a/builtin/merge-ours.c\n+++ b/builtin/merge-ours.c\n@@ -18,7 +18,6 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix)\n {\n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(builtin_merge_ours_usage);\n-\n \t/*\n \t * The contents of the current index becomes the tree we\n \t * commit.  The index must match HEAD, or this merge cannot go\ndiff --git a/builtin/merge-theirs.c b/builtin/merge-theirs.c\nnew file mode 100644\nindex 0000000000..545680ebc6\n--- /dev/null\n+++ b/builtin/merge-theirs.c\n@@ -0,0 +1,72 @@\n+/*\n+ * Copyright (c) 2018 Ævar Arnfjörð Bjarmason\n+ *\n+ * Resolve the merge by picking the Nth (per -X) parent's tree is our\n+ * new tree.\n+ */\n+#include \"git-compat-util.h\"\n+#include \"builtin.h\"\n+#include \"run-command.h\"\n+#include \"diff.h\"\n+\n+static const char builtin_merge_theirs_usage[] =\n+\t\"git merge-theirs <base>... -- HEAD <remote>...\";\n+\n+static void read_tree_hex_oid(const char *hex_oid)\n+{\n+\tint i = 0;\n+\tconst char *args[4];\n+\n+\targs[i++] = \"read-tree\";\n+\targs[i++] = hex_oid;\n+\targs[i] = NULL;\n+\n+\tif (run_command_v_opt(args, RUN_GIT_CMD))\n+\t\tdie(_(\"read-tree failed\"));\n+}\n+\n+int cmd_merge_theirs(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char *mainline_str;\n+\tconst int argc_offset = 3;\n+\tchar *end;\n+\tint mainline;\n+\tconst char *branch;\n+\tstruct object_id commit;\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage(builtin_merge_theirs_usage);\n+\tif (argc < 6)\n+\t\tusage(builtin_merge_theirs_usage);\n+\n+\t/*\n+\t * Parse the --N part of `git merge-theirs --N base -- HEAD\n+\t * other-branch [other-branch-2 ...]`.\n+\t *\n+\t * TODO: Handle no -X argument being equivalent to -X 2.\n+\t */\n+\tmainline_str = argv[1];\n+\tif (!mainline_str[2])\n+\t\tusage(builtin_merge_theirs_usage);\n+\tmainline = strtol(mainline_str + 2, &end, 10);\n+\tif (*end || mainline <= 0)\n+\t\tdie(_(\"'-s theirs -X N' expects a number greater than zero\"));\n+\tif (mainline >= (argc - argc_offset))\n+\t\tdie(_(\"'-s theirs -X N' must come with a corresponding Nth commit to merge!\"));\n+\n+\t/* Have the branch name */\n+\tbranch = argv[argc_offset + mainline];\n+\tif (get_oid(branch, &commit))\n+\t\tdie(_(\"could not resolve ref '%s'\"), branch);\n+\n+\t/* Final sanity checks, copied from merge-ours.c */\n+\tif (read_cache() < 0)\n+\t\tdie_errno(\"read_cache failed\");\n+\tif (index_differs_from(\"HEAD\", NULL, 0))\n+\t\texit(2);\n+\n+\t/* Read the Nth tree */\n+\tread_tree_hex_oid(oid_to_hex(&commit));\n+\n+\texit(0);\n+}\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9db5a2cf16..dd623530d8 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -81,6 +81,7 @@ static struct strategy all_strategy[] = {\n \t{ \"octopus\",    DEFAULT_OCTOPUS },\n \t{ \"resolve\",    0 },\n \t{ \"ours\",       NO_FAST_FORWARD | NO_TRIVIAL },\n+\t{ \"theirs\",     NO_FAST_FORWARD | NO_TRIVIAL },\n \t{ \"subtree\",    NO_FAST_FORWARD | NO_TRIVIAL },\n };\n\ndiff --git a/git.c b/git.c\nindex 3a89893712..8e87accbb9 100644\n--- a/git.c\n+++ b/git.c\n@@ -434,6 +434,7 @@ static struct cmd_struct commands[] = {\n \t{ \"merge-recursive-ours\", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },\n \t{ \"merge-recursive-theirs\", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },\n \t{ \"merge-subtree\", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },\n+\t{ \"merge-theirs\", cmd_merge_theirs, RUN_SETUP | NO_PARSEOPT },\n \t{ \"merge-tree\", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },\n \t{ \"mktag\", cmd_mktag, RUN_SETUP | NO_PARSEOPT },\n \t{ \"mktree\", cmd_mktree, RUN_SETUP },\n"},{"id":"345042","messageId":"CAGZ79kak57D2_9bx5KwefFEZ0=Dk2bbZ=n8YFnByChK=6Hj2HQ@mail.gmail.com","threadId":"48322","inReplyTo":"87sh7sdtc1.fsf@evledraar.gmail.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-04-18T23:22:15Z","receivedAt":"2018-04-18T23:22:20Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Apr 18, 2018 at 3:48 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> We have a -s ours, but not a -s theirs. This is a WIP patch to implement\n> that. It works, but I haven't dealt with this part of the internal API\n> before, comments most welcome.\n\nI hope reference pointers are welcome, too.\nhttps://public-inbox.org/git/xmqqzi9iazrp.fsf@gitster.mtv.corp.google.com/\n"},{"id":"345061","messageId":"xmqqy3hjes3n.fsf@gitster-ct.c.googlers.com","threadId":"48322","inReplyTo":"87sh7sdtc1.fsf@evledraar.gmail.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-19T04:29:48Z","receivedAt":"2018-04-19T04:29:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Questions:\n>\n>  1. Should I be calling read-tree here with run_command_v_opt(), or is\n>     there some internal API I should be using?\n\nThe internal is unpack_trees(), which is usabe as a library-ish API\nreasonably cleanly and easily.  For a project large enough where the\nperforce can become an issue, the overhead to spawn read-tree as an\nexternal process would be dwarfed by the cost of real processing of\nmerging multiple trees into an in-core index, but it involves two\nextra writing and reading the index file back and forth compared to\nthe approach to use unpack_trees() to do everything in core.  As\nlong as the \"now make sure that the contents of the index file is\nthat of the tree of the N-th parent\" code is cleanly isolated in the\nimplementation, it is probably better to refrain from premature\noptimization.\n\n>  2. Currently merge-ours is just a no-op since we take the current HEAD,\n>     but maybe it would be cleaner to implement it in terms of this\n>     thing, also to get immediate test coverage for all the -s ours\n>     stuff. We'd be reading the tree redundantly into the index, but\n>     maybe it's worth it for the coverage...\n\nI doubt that it would be a sensible approach.  If anything, even if\nwe lived in an alternate universe where \"-s ours\" weren't invented\nand \"-s become-nth-tree -W $N\" feature gets first introduced, I\nwould imagine that we would introduce a code to special case \"-s\nours\" (aka \"take the current HEAD\") as an obvious optimization for\nthe common and trivial case, essentially splitting the \"unification\"\nyou are suggesting here.\n\n>  3. Is there a better name for this than -s theirs? Maybe `-s nth -X N`?\n\nI tend to agree that \"-s thiers -X N\" is horrible; \"-s ours -X N\"\nwould slightly be a better choice as it does not have to introduce a\nnew option.  \"-s nth -X N\" does not sound all that bad.\n\n\"Does this feature make sense?\" was not among the questions listed,\nand I am not ready to answer to it yet anyway, so ...\n\n"},{"id":"345062","messageId":"xmqqpo2verc6.fsf@gitster-ct.c.googlers.com","threadId":"48322","inReplyTo":"87sh7sdtc1.fsf@evledraar.gmail.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-19T04:46:17Z","receivedAt":"2018-04-19T04:46:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> We have a -s ours, but not a -s theirs. This is a WIP patch to implement\n> that. It works, but I haven't dealt with this part of the internal API\n> before, comments most welcome.\n>\n> The purpose of this is that I'm working with a rollout tool that is\n> capable of doing hotfixes on top of old commits on \"master\".\n>\n> It does this by cherry-picking a commit from origin/master, and then\n> merges it with origin/master & pushes it back, before finally reset\n> --hard to the cherry-pick & rolling out.\n>\n> The reason it's doing this is to maintain the guarantee that all rolled\n> out commits are reachable from \"master\", and to handle the more general\n> case where original work is made during a hotfix, we don't want to then\n> do a subsequent \"normal\" rollout and miss the fix.\n\nThis question has nothing to do with your \"-s theirs\" but let me see\nif I got the above correctly.  Suppose you have a deployed branch\n(say, \"prod\"), all developments happen on \"master\" elsewhere that\ncan be seen as \"origin/master\", so you may have a few fixes that is\nnot yet in \"prod\" you would want to cherry-pick from origin/master.\n\n    $ git checkout prod\n    $ git cherry-pick origin/master~2\n    $ git cherry-pick origin/master\n\nLet's say that \"master\" had a fix at HEAD~2, HEAD~1 is a feature\nenhancement that is not yet ready for \"prod\", and HEAD is another\nfix.  Up to this point you successfully back-ported the fixes to\n\"prod\".\n\nThen you do merge the tip into \"master\", i.e.\n\n    $ git checkout origin/master && git merge -s ours prod\n    $ git push origin HEAD:master\n    $ git checkout prod\n\nto make sure that the \"master\" at the source of truth knows that\nit already has what our \"prod\" with these two cherry-picks have.\n\nIs that what is going on here?\n\nI am just wondering what would and should happen to the non-fix\ncommit in the middle in the above example.  Perhaps your workflow\nautomatically does the right thing to it, perhaps not.\n\n\n[Footnote]\n\nObviously you can do this the other way around if you had \"-s\ntheirs\", i.e. instead of the last two lines from the above sequence,\nyou could do\n\n    $ git merge -s nth -X 2 origin/master\n    $ git push origin HEAD:master\n    $ git reset --hard HEAD@{1}\n\nbut it is not all that interesting (at least to me) either way, as a\nlarger issue with the above I'd imagine people would see is that\neven temporarily you would expose \"master\" material in that working\ntree you usually have \"prod\" checkout.  That would irritate those\nwho consider that \"push to deploy\" aka \"live site is actually a\nworking tree\" is sensible more than the lack of \"-s theirs\" I would\nthink.\n"},{"id":"345079","messageId":"87r2nbeh1r.fsf@evledraar.gmail.com","threadId":"48322","inReplyTo":"xmqqpo2verc6.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-19T08:28:32Z","receivedAt":"2018-04-19T08:28:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 19 2018, Junio C. Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> We have a -s ours, but not a -s theirs. This is a WIP patch to implement\n>> that. It works, but I haven't dealt with this part of the internal API\n>> before, comments most welcome.\n>>\n>> The purpose of this is that I'm working with a rollout tool that is\n>> capable of doing hotfixes on top of old commits on \"master\".\n>>\n>> It does this by cherry-picking a commit from origin/master, and then\n>> merges it with origin/master & pushes it back, before finally reset\n>> --hard to the cherry-pick & rolling out.\n>>\n>> The reason it's doing this is to maintain the guarantee that all rolled\n>> out commits are reachable from \"master\", and to handle the more general\n>> case where original work is made during a hotfix, we don't want to then\n>> do a subsequent \"normal\" rollout and miss the fix.\n>\n> This question has nothing to do with your \"-s theirs\" but let me see\n> if I got the above correctly.  Suppose you have a deployed branch\n> (say, \"prod\"), all developments happen on \"master\" elsewhere that\n> can be seen as \"origin/master\", so you may have a few fixes that is\n> not yet in \"prod\" you would want to cherry-pick from origin/master.\n>\n>     $ git checkout prod\n>     $ git cherry-pick origin/master~2\n>     $ git cherry-pick origin/master\n>\n> Let's say that \"master\" had a fix at HEAD~2, HEAD~1 is a feature\n> enhancement that is not yet ready for \"prod\", and HEAD is another\n> fix.  Up to this point you successfully back-ported the fixes to\n> \"prod\".\n>\n> Then you do merge the tip into \"master\", i.e.\n>\n>     $ git checkout origin/master && git merge -s ours prod\n>     $ git push origin HEAD:master\n>     $ git checkout prod\n>\n> to make sure that the \"master\" at the source of truth knows that\n> it already has what our \"prod\" with these two cherry-picks have.\n>\n> Is that what is going on here?\n>\n> I am just wondering what would and should happen to the non-fix\n> commit in the middle in the above example.  Perhaps your workflow\n> automatically does the right thing to it, perhaps not.\n>\n>\n> [Footnote]\n>\n> Obviously you can do this the other way around if you had \"-s\n> theirs\", i.e. instead of the last two lines from the above sequence,\n> you could do\n>\n>     $ git merge -s nth -X 2 origin/master\n>     $ git push origin HEAD:master\n>     $ git reset --hard HEAD@{1}\n>\n> but it is not all that interesting (at least to me) either way, as a\n> larger issue with the above I'd imagine people would see is that\n> even temporarily you would expose \"master\" material in that working\n> tree you usually have \"prod\" checkout.  That would irritate those\n> who consider that \"push to deploy\" aka \"live site is actually a\n> working tree\" is sensible more than the lack of \"-s theirs\" I would\n> think.\n\nYeah this -s theirs is redundant to just doing it the other way around\nas you describe.\n\nThe reason I want it is to always do the hotfix merge the same way\nwhether I'm dealing with a case where there's original work in the\nhotfix (rare) or the case where there's just stuff to \"prod\"\ncherry-picked from \"master\" (common).\n\nI.e. I have:\n\n 1. No original work on the hotfix. As determined by comparing the\n    patch-id output of @{u}.. and ..@{u} and seeing if the patch ids I\n    have cherry-picked are from commits that exist since there was last\n    a full rollout.\n\n 2. Original work during the hotfix on top of \"prod\", which we'll then\n    want in the next rollout (when it'll be synced with \"master\").\n\nOnly #1 should use `-s theirs -X 2`, but #2 will just use the normal\nmerge strategy, i.e. it's possible we'll conflict, but then we should\nresolve the conflict and push the fix to \"master\" (or at least\nexplicitly decide not to keep it).\n\nI think that supporting this use-case explicitly in git is better than\nhaving some unintuitive workaround where I'll first need to check out\nthe other branch purely because git-merge has an artificial limitation\nof the \"ours\" driver having no mode to pick the Nth commit.\n"},{"id":"345179","messageId":"xmqqpo2ud9ih.fsf@gitster-ct.c.googlers.com","threadId":"48322","inReplyTo":"87r2nbeh1r.fsf@evledraar.gmail.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-20T00:08:54Z","receivedAt":"2018-04-20T00:09:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Apr 19 2018, Junio C. Hamano wrote:\n>\n>> This question has nothing to do with your \"-s theirs\" but let me see\n>> if I got the above correctly.  Suppose you have a deployed branch\n>> (say, \"prod\"), all developments happen on \"master\" elsewhere that\n>> can be seen as \"origin/master\", so you may have a few fixes that is\n>> not yet in \"prod\" you would want to cherry-pick from origin/master.\n>>\n>>     $ git checkout prod\n>>     $ git cherry-pick origin/master~2\n>>     $ git cherry-pick origin/master\n>>\n>> Let's say that \"master\" had a fix at HEAD~2, HEAD~1 is a feature\n>> enhancement that is not yet ready for \"prod\", and HEAD is another\n>> fix.  Up to this point you successfully back-ported the fixes to\n>> \"prod\".\n>>\n>> Then you do merge the tip into \"master\", i.e.\n>>\n>>     $ git checkout origin/master && git merge -s ours prod\n>>     $ git push origin HEAD:master\n>>     $ git checkout prod\n>>\n>> to make sure that the \"master\" at the source of truth knows that\n>> it already has what our \"prod\" with these two cherry-picks have.\n>>\n>> Is that what is going on here?\n>>\n>> I am just wondering what would and should happen to the non-fix\n>> commit in the middle in the above example.  Perhaps your workflow\n>> automatically does the right thing to it, perhaps not.\n>>\n>>\n>> [Footnote]\n>> ...\n> Yeah this -s theirs is redundant to just doing it the other way around\n> as you describe.\n> ...\n\nHeh, you responded to a much less relevant footnote without\naddressing the main part of the message which was a more interesting\nquestion to me ;-)\n"},{"id":"345206","messageId":"87muxydw0w.fsf@evledraar.gmail.com","threadId":"48322","inReplyTo":"xmqqpo2verc6.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC WIP PATCH] merge: implement -s theirs -X N","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-20T10:14:55Z","receivedAt":"2018-04-20T10:15:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 19 2018, Junio C. Hamano wrote:\n\nI suppose this is more in reply to\nxmqqpo2ud9ih.fsf@gitster-ct.c.googlers.com. I was trying to answer all\nyour questions in my 87r2nbeh1r.fsf@evledraar.gmail.com, but I think it\nwasn't clear, hopefully this inline clears things up.\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> We have a -s ours, but not a -s theirs. This is a WIP patch to implement\n>> that. It works, but I haven't dealt with this part of the internal API\n>> before, comments most welcome.\n>>\n>> The purpose of this is that I'm working with a rollout tool that is\n>> capable of doing hotfixes on top of old commits on \"master\".\n>>\n>> It does this by cherry-picking a commit from origin/master, and then\n>> merges it with origin/master & pushes it back, before finally reset\n>> --hard to the cherry-pick & rolling out.\n>>\n>> The reason it's doing this is to maintain the guarantee that all rolled\n>> out commits are reachable from \"master\", and to handle the more general\n>> case where original work is made during a hotfix, we don't want to then\n>> do a subsequent \"normal\" rollout and miss the fix.\n>\n> This question has nothing to do with your \"-s theirs\" but let me see\n> if I got the above correctly.  Suppose you have a deployed branch\n> (say, \"prod\"), all developments happen on \"master\" elsewhere that\n> can be seen as \"origin/master\", so you may have a few fixes that is\n> not yet in \"prod\" you would want to cherry-pick from origin/master.\n>\n>     $ git checkout prod\n>     $ git cherry-pick origin/master~2\n>     $ git cherry-pick origin/master\n>\n> Let's say that \"master\" had a fix at HEAD~2, HEAD~1 is a feature\n> enhancement that is not yet ready for \"prod\", and HEAD is another\n> fix.  Up to this point you successfully back-ported the fixes to\n> \"prod\".\n>\n> Then you do merge the tip into \"master\", i.e.\n>\n>     $ git checkout origin/master && git merge -s ours prod\n>     $ git push origin HEAD:master\n>     $ git checkout prod\n>\n> to make sure that the \"master\" at the source of truth knows that\n> it already has what our \"prod\" with these two cherry-picks have.\n>\n> Is that what is going on here?\n\nYes, the idea is that all commits that are rolled out are reachable from\n\"master\". In my case there's no \"prod\" branch.\n\nInstead we just have a tag on an older version of \"master\", and we're\napplying a hotfix (usually one cherry-picked commit) to that tag.\n\nWe then push a merge of that origin/master back to origin/master so that:\n\n 1. The commit is reachable from \"master\" as discussed. I.e. a \"git\n    clone\" with the default tag follow logic will bring in that tag & I\n    just need to bring in master to bring in every thing ever rolled\n    out, not bring in every tag.\n\n 2. Because the working tree is now in a state where we're at that\n    hotfix tag, and we want to fast-forward to origin/master on the next\n    \"real\" rollout.\n\n    We could just 'reset --hard' in those cases, but it's easier to\n    always be able to do a \"git pull\" (except when initiating a hotfix).\n\n> I am just wondering what would and should happen to the non-fix\n> commit in the middle in the above example.  Perhaps your workflow\n> automatically does the right thing to it, perhaps not.\n\nIf there's a non-fix commit that contains any original work in the\nhotfix I won't be able to use this ~-s theirs\" strategy, and I detect\nsuch case swith the patch-id method discussed in\n87r2nbeh1r.fsf@evledraar.gmail.com. Then we'll need to use the default\nmerge logic.\n\nThat's actually what we're doing now, but after running into several\nbugs with merges gone wrong I'm hoping to fix it, and without bending\nover backwards to use -s ours by doing the merge the other way around.\n"}]}