{"thread":{"id":"50603","subject":"[BUG] All files in folder are moved when cherry-picking commit that moves fewer files","startedAt":"2019-02-27T12:52:37Z","lastAt":"2019-04-05T16:32:21Z","messageCount":49,"participants":["Linus Nilsson","Phillip Wood","Elijah Newren","Jeff King","Junio C Hamano","Ævar Arnfjörð Bjarmason","Jacob Keller"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"370302","messageId":"67C5CE5D6EC13D4DB8FC51E2F9BD262F022B8B3151@Triton.ad.trimma.se","threadId":"50603","inReplyTo":null,"subject":"[BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Linus Nilsson","fromEmail":"linus.nilsson@trimma.se","sentAt":"2019-02-27T12:47:07Z","receivedAt":"2019-02-27T12:52:37Z","isPatch":false,"sender":{"key":"linus.nilsson@trimma.se","avatar":null},"body":"Hi\n\nI have found what I suspect to be a bug, or at least not desirable behavior in my case. In one branch, I have moved all files in a directory to another directory. The first directory is now empty in this branch (I haven't tested whether this is significant). I cherry-picked the commit to another branch that has additional files in the first directory. All the files in the first directory are now moved to the second directory, and not just the files that were moved in the original commit. It happens in Git for Windows, versions 2.20.1 and 2.21.0.\n\nIt can be reproduced using these steps:\n\n$ git init\n$ mkdir a\n$ echo Hello > a/file1\n$ git add a/file1\n$ git commit -m 'Add file1'\n$ git checkout -b fix\n$ mkdir b\n$ git mv a/file1 b\n$ git commit -m 'Move file1 to b'\n$ git checkout master\n$ echo Bye > a/file2\n$ git add a/file2\n$ git commit -m 'Add file2'\n$ git cherry-pick fix\n[master 5b63afb] Move file1 to b\n Date: Wed Feb 27 13:27:45 2019 +0100\n 2 files changed, 0 insertions(+), 0 deletions(-)\n rename {a => b}/file1 (100%)\n rename {a => b}/file2 (100%)\n\nRegards\nLinus Nilsson\n"},{"id":"370309","messageId":"512993fe-51da-745d-031b-37ea162499fb@gmail.com","threadId":"50603","inReplyTo":"67C5CE5D6EC13D4DB8FC51E2F9BD262F022B8B3151@Triton.ad.trimma.se","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-02-27T14:30:54Z","receivedAt":"2019-02-27T14:31:00Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Linus\n\nOn 27/02/2019 12:47, Linus Nilsson wrote:\n> Hi\n> \n> I have found what I suspect to be a bug, or at least not desirable behavior in my case. In one branch, I have moved all files in a directory to another directory. The first directory is now empty in this branch (I haven't tested whether this is significant). \n\nI suspect that because you've moved all the files git thinks the \ndirectory has been renamed and that's why it moves a/file2 when fix is \ncherry-picked in the example below. I've cc'd Elijah as he knows more \nabout how the directory rename detection works.\n\nBest Wishes\n\nPhillip\n\nI cherry-picked the commit to another branch that has additional files \nin the first directory. All the files in the first directory are now \nmoved to the second directory, and not just the files that were moved in \nthe original commit. It happens in Git for Windows, versions 2.20.1 and \n2.21.0.\n> \n> It can be reproduced using these steps:\n> \n> $ git init\n> $ mkdir a\n> $ echo Hello > a/file1\n> $ git add a/file1\n> $ git commit -m 'Add file1'\n> $ git checkout -b fix\n> $ mkdir b\n> $ git mv a/file1 b\n> $ git commit -m 'Move file1 to b'\n> $ git checkout master\n> $ echo Bye > a/file2\n> $ git add a/file2\n> $ git commit -m 'Add file2'\n> $ git cherry-pick fix\n> [master 5b63afb] Move file1 to b\n>   Date: Wed Feb 27 13:27:45 2019 +0100\n>   2 files changed, 0 insertions(+), 0 deletions(-)\n>   rename {a => b}/file1 (100%)\n>   rename {a => b}/file2 (100%)\n> \n> Regards\n> Linus Nilsson\n> \n"},{"id":"370316","messageId":"CABPp-BGbrWrS0622yfTRUqRzWzNsZ_v9cAWW6ucXBYXqD26qHA@mail.gmail.com","threadId":"50603","inReplyTo":"512993fe-51da-745d-031b-37ea162499fb@gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-02-27T16:02:35Z","receivedAt":"2019-02-27T16:02:51Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi,\n\nOn Wed, Feb 27, 2019 at 6:30 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Linus\n>\n> On 27/02/2019 12:47, Linus Nilsson wrote:\n> > Hi\n> >\n> > I have found what I suspect to be a bug, or at least not desirable behavior in my case. In one branch, I have moved all files in a directory to another directory. The first directory is now empty in this branch (I haven't tested whether this is significant).\n>\n> I suspect that because you've moved all the files git thinks the\n> directory has been renamed and that's why it moves a/file2 when fix is\n> cherry-picked in the example below. I've cc'd Elijah as he knows more\n> about how the directory rename detection works.\n\nYes, Phillip is correct.  If the branch you were\nmerging/cherry-picking still had any files at all in the original\ndirectory, then no directory rename would be detected.  You can read\nup more details about how it works at\nhttps://git.kernel.org/pub/scm/git/git.git/tree/Documentation/technical/directory-rename-detection.txt\n"},{"id":"370323","messageId":"20190227164056.GA2062@sigill.intra.peff.net","threadId":"50603","inReplyTo":"CABPp-BGbrWrS0622yfTRUqRzWzNsZ_v9cAWW6ucXBYXqD26qHA@mail.gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-02-27T16:40:56Z","receivedAt":"2019-02-27T16:41:00Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 27, 2019 at 08:02:35AM -0800, Elijah Newren wrote:\n\n> > > I have found what I suspect to be a bug, or at least not desirable\n> > > behavior in my case. In one branch, I have moved all files in a\n> > > directory to another directory. The first directory is now empty\n> > > in this branch (I haven't tested whether this is significant).\n> >\n> > I suspect that because you've moved all the files git thinks the\n> > directory has been renamed and that's why it moves a/file2 when fix is\n> > cherry-picked in the example below. I've cc'd Elijah as he knows more\n> > about how the directory rename detection works.\n> \n> Yes, Phillip is correct.  If the branch you were\n> merging/cherry-picking still had any files at all in the original\n> directory, then no directory rename would be detected.  You can read\n> up more details about how it works at\n> https://git.kernel.org/pub/scm/git/git.git/tree/Documentation/technical/directory-rename-detection.txt\n\nIs there a way to disable it (either by config, or for a single run)? I\nknow there's merge.renames, but it's plausible somebody might want\nfile-level renames but not directory-level ones.\n\n-Peff\n"},{"id":"370328","messageId":"CABPp-BGOqQ0P8ywCK_sybPOeASxPnkTq7NXW8678f=345=68-Q@mail.gmail.com","threadId":"50603","inReplyTo":"20190227164056.GA2062@sigill.intra.peff.net","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-02-27T17:31:38Z","receivedAt":"2019-02-27T17:31:53Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Feb 27, 2019 at 8:40 AM Jeff King <peff@peff.net> wrote:\n>\n> On Wed, Feb 27, 2019 at 08:02:35AM -0800, Elijah Newren wrote:\n>\n> > > > I have found what I suspect to be a bug, or at least not desirable\n> > > > behavior in my case. In one branch, I have moved all files in a\n> > > > directory to another directory. The first directory is now empty\n> > > > in this branch (I haven't tested whether this is significant).\n> > >\n> > > I suspect that because you've moved all the files git thinks the\n> > > directory has been renamed and that's why it moves a/file2 when fix is\n> > > cherry-picked in the example below. I've cc'd Elijah as he knows more\n> > > about how the directory rename detection works.\n> >\n> > Yes, Phillip is correct.  If the branch you were\n> > merging/cherry-picking still had any files at all in the original\n> > directory, then no directory rename would be detected.  You can read\n> > up more details about how it works at\n> > https://git.kernel.org/pub/scm/git/git.git/tree/Documentation/technical/directory-rename-detection.txt\n>\n> Is there a way to disable it (either by config, or for a single run)? I\n> know there's merge.renames, but it's plausible somebody might want\n> file-level renames but not directory-level ones.\n>\n> -Peff\n\nNot yet.  Adding such an option, similar in nature to the flags for\nturning off renaming detection entirely (merge.renames, diff.renames,\n-Xno-renames) would probably make sense (I don't see an analogy to\n-Xrename-threshold=, though).  It might make sense as just an\nalternate setting of merge.renames or diff.renames, though it's\npossible that could get confusing with \"copy\" being an option.\n#leftoverbits for someone that wants to figure out what the option\nnames and values should be?\n"},{"id":"370355","messageId":"67C5CE5D6EC13D4DB8FC51E2F9BD262F022B8B3599@Triton.ad.trimma.se","threadId":"50603","inReplyTo":"CABPp-BGOqQ0P8ywCK_sybPOeASxPnkTq7NXW8678f=345=68-Q@mail.gmail.com","subject":"RE: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Linus Nilsson","fromEmail":"linus.nilsson@trimma.se","sentAt":"2019-02-28T08:16:43Z","receivedAt":"2019-02-28T08:16:50Z","isPatch":false,"sender":{"key":"linus.nilsson@trimma.se","avatar":null},"body":"Thanks for the answers. So it seems it's not a bug, but may lead to new merge options. I worked around it anyway, so it was not a real problem.\n\nMed vänlig hälsning\nLinus Nilsson\n\n-----Original Message-----\nFrom: Elijah Newren <newren@gmail.com> \nSent: Wednesday, 27 February 2019 18:32\nTo: Jeff King <peff@peff.net>\nCc: Phillip Wood <phillip.wood@dunelm.org.uk>; Linus Nilsson <Linus.Nilsson@trimma.se>; git@vger.kernel.org\nSubject: Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files\n\nOn Wed, Feb 27, 2019 at 8:40 AM Jeff King <peff@peff.net> wrote:\n>\n> On Wed, Feb 27, 2019 at 08:02:35AM -0800, Elijah Newren wrote:\n>\n> > > > I have found what I suspect to be a bug, or at least not \n> > > > desirable behavior in my case. In one branch, I have moved all \n> > > > files in a directory to another directory. The first directory \n> > > > is now empty in this branch (I haven't tested whether this is significant).\n> > >\n> > > I suspect that because you've moved all the files git thinks the \n> > > directory has been renamed and that's why it moves a/file2 when \n> > > fix is cherry-picked in the example below. I've cc'd Elijah as he \n> > > knows more about how the directory rename detection works.\n> >\n> > Yes, Phillip is correct.  If the branch you were \n> > merging/cherry-picking still had any files at all in the original \n> > directory, then no directory rename would be detected.  You can read \n> > up more details about how it works at \n> > https://git.kernel.org/pub/scm/git/git.git/tree/Documentation/techni\n> > cal/directory-rename-detection.txt\n>\n> Is there a way to disable it (either by config, or for a single run)? \n> I know there's merge.renames, but it's plausible somebody might want \n> file-level renames but not directory-level ones.\n>\n> -Peff\n\nNot yet.  Adding such an option, similar in nature to the flags for turning off renaming detection entirely (merge.renames, diff.renames,\n-Xno-renames) would probably make sense (I don't see an analogy to -Xrename-threshold=, though).  It might make sense as just an alternate setting of merge.renames or diff.renames, though it's possible that could get confusing with \"copy\" being an option.\n#leftoverbits for someone that wants to figure out what the option names and values should be?\n"},{"id":"370402","messageId":"xmqq7edj9uh2.fsf@gitster-ct.c.googlers.com","threadId":"50603","inReplyTo":"CABPp-BGOqQ0P8ywCK_sybPOeASxPnkTq7NXW8678f=345=68-Q@mail.gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-01T02:52:41Z","receivedAt":"2019-03-01T02:52:46Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"As you know that I've always been skeptical to this rename-directory\nbusiness, this probably won't come as a surprise, but I seriously\nthink directory renames should be made opt-in, and as a separate\noption, making the option three-way.  I.e.\n\n - do we do any renames (yes/no)?\n\n - if we do do renames, do we base the decision only on the contents\n   of the thing along, or do we allow neighbouring files affect the\n   decision?\n \nThat is, in addition to the traditional --renames or -M, we'd have a\nseparate bool --allow-directory-renames that is by default off and\nis a no-op if the former is off.\n\nWe had to fix a breakage recently for 3-way fallback case, and we\nexplained the fix as needed because the case lacks the full\ninformation, but I think even with the full information at hand, the\nrename-directory heurisitcs is inherently riskier than the content\nbased rename detection.\n\nSuppose you had F1, F2, ... in directory D1, and moved them all to\nD1/D2.  In the meantime, somebody else adds Fn to directory D1.  It\nmay be likely that some variant of Fn would want to go to D1/D2, but\nit also is very likely that there should be a difference between\nD1/Fn somebody else had, and the contents of D1/D2/Fn in the merge\nresult.  Perhaps D1/F1 in your preimage used to refer to another\npath in the top-level directory as \"../top\", but the reference would\nhave been rewritten to \"../../top\" when you moved D1/F1 to D1/D2/F1,\nand the person doing the merge should at least check if D1/Fn that\ncomes from the other party needs a similar adjustment while merged.\n\nIn the above scenario, if there were D1/Fn in _your_ preimage and\nall the other party did was to make in-place modification, the story\nis vastly different.  Most likely you would have made most of, if\nnot all, the adjustment necessary for D1/Fn to sit in its new\nlocation, while the other party kept the relative reference to other\nplaces intact, so we can say that both parties have say in the\ncontents of the auto-merged result.  The \"since neighgours moved,\nthis must also want to move the same way\" heuristics does not give a\nchance to the party that is not aware of the move to prepare the\ncontents appropriate for the new location, by definition, so the\nonus is on the person who merges to adjust the contents.\n\nThanks.\n\n[jch: I am still mostly offline til the next week, but I had a\nchance to sit in front of my mailbox long enough, so...]\n"},{"id":"370480","messageId":"CABPp-BFdbC2Shq8BrP=Ht21ZbjvEZVL_uQC0=3_YwbLJVbBU+g@mail.gmail.com","threadId":"50603","inReplyTo":"xmqq7edj9uh2.fsf@gitster-ct.c.googlers.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-02T23:48:17Z","receivedAt":"2019-03-02T23:48:33Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Junio,\n\nOn Thu, Feb 28, 2019 at 6:52 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> As you know that I've always been skeptical to this rename-directory\n> business, this probably won't come as a surprise, but I seriously\n> think directory renames should be made opt-in, and as a separate\n> option, making the option three-way.  I.e.\n>\n>  - do we do any renames (yes/no)?\n>\n>  - if we do do renames, do we base the decision only on the contents\n>    of the thing along, or do we allow neighbouring files affect the\n>    decision?\n>\n> That is, in addition to the traditional --renames or -M, we'd have a\n> separate bool --allow-directory-renames that is by default off and\n> is a no-op if the former is off.\n\nWe've gone from always-off (ability wasn't implemented), to always on\n(assuming normal rename detection was on), and now we're considering\nmaking it an option, with you proposing opt-in.  While I think opt-out\nwould be a better default, we may want to first consider the range of\npossibilities; if more safety is needed, other choices might be safer\nthan either opt-in or opt-out.\n\nThis email reminds me a bit of the recent overlay discussion, where\nsome of your wording made me think about directory handling, but you\nhad smudge filters in mind.  We're occasionally on different\nwavelengths, and I'm worried I might be mis-reading your main point\nand/or that my responses only address part of the issues you have in\nmind.  Let me know if that's the case.\n\n> We had to fix a breakage recently for 3-way fallback case, and we\n> explained the fix as needed because the case lacks the full\n> information, but I think even with the full information at hand, the\n> rename-directory heurisitcs is inherently riskier than the content\n> based rename detection.\n>\n> Suppose you had F1, F2, ... in directory D1, and moved them all to\n> D1/D2.  In the meantime, somebody else adds Fn to directory D1.  It\n> may be likely that some variant of Fn would want to go to D1/D2, but\n> it also is very likely that there should be a difference between\n> D1/Fn somebody else had, and the contents of D1/D2/Fn in the merge\n> result.  Perhaps D1/F1 in your preimage used to refer to another\n> path in the top-level directory as \"../top\", but the reference would\n> have been rewritten to \"../../top\" when you moved D1/F1 to D1/D2/F1,\n> and the person doing the merge should at least check if D1/Fn that\n> comes from the other party needs a similar adjustment while merged.\n>\n> In the above scenario, if there were D1/Fn in _your_ preimage and\n> all the other party did was to make in-place modification, the story\n> is vastly different.  Most likely you would have made most of, if\n> not all, the adjustment necessary for D1/Fn to sit in its new\n> location, while the other party kept the relative reference to other\n> places intact, so we can say that both parties have say in the\n> contents of the auto-merged result.  The \"since neighgours moved,\n> this must also want to move the same way\" heuristics does not give a\n> chance to the party that is not aware of the move to prepare the\n> contents appropriate for the new location, by definition, so the\n> onus is on the person who merges to adjust the contents.\n\nThere are a few issues here to unpack.\n\nFirst, in regards to your example, the very first \"Limitation\" imposed\nby the directory rename heuristics was:\n  * If a given directory still exists on both sides of a merge, we do\nnot consider it to have been renamed.\nTherefore, your (detailed) example above of \"renaming\" D1/* to D1/D2/*\nisn't something the directory rename heuristics supports (the presence\nof D1/D2/ implies D1/ still exists and thus could not have been\nrenamed; this particular case is explicitly mentioned in t6043 and I\nbrought it up a couple times during the review of the directory rename\npatch series with Stefan).  There are too many edge/corner cases that\nbecome difficult without this rule and it was considered more likely\nto (negatively) surprise users, so even with directory rename\nheuristics on, a new file Fn added to D1/ on the other side of the\nmerge will remain in D1/Fn after the merge.  Perhaps this\nclarification helps assuage your worries, or maybe your example was an\nunfortunate one for relaying your real underlying concern.\n\nSecond, you stated that you thought \"rename-directory [heuristics are]\ninherently riskier\" than not having them on, and then gave a single\n(though involved) example.  This could be read to imply that you\nbelieve directory rename heuristics are for convenience only, that\nthere is no risk involved with not detecting directory renames, and\nthus all that matters is the cost benefit of the convenience of\ndetecting renames for users vs. whatever risks there are in\n\"detecting\" renames that aren't actually wanted by users.  I do not\nknow if you intended any of these implications at all, but I do want\nto point out that not detecting directory renames is not merely a\nconvenience.  While that convenience was _part_ of the reason spurring\nmy work on that capability, it was also pushed due to my having been\ntold about bugs caused by not having it.  I don't remember the details\nI was told (and I wasn't even told the full details of the bugs), but\nmy understanding and/or guesses years later is:\n  * build systems sometimes build code according to directories and globs\n  * an un-detected directory rename can thus cause new files on one\nside of history to not get built as expected\n  * other files may not have direct references to identifiers in the\nnew files, thus no compilation-time or link-time failures occur\n  * the new code could have had a static singleton that registered\nitself with some other logic in the code\n  * the \"other logic\" rather than having a one-to-one set of handlers\nmay have a priority listing of which static singletons to use\n  * therefore, there is no runtime failure from failing to compile the\ncode either, just different behavior than expected\n  * a testcase should have caught this type of problem anyway, but\ntestcases aren't always added\n  * it was a rarely used, but important case\n  * the developer did *manually* test this pretty carefully...but\ndidn't expect a \"clean merge\" to break things.  :-(\n  * this kind of issue might not be detected before release,\ndeployment, or installation and usage at a customer site.\n  * testcases are a best practice for a reason, it was totally our\nfault for missing them, but having multiple LTS releases and large\ncode directory refactors between versions increased the odds we'd\neventually hit failures of this type\n\nThird, at the end of your example, you say that the rename-directory\nheuristics \"[puts the onus] on the person who merges to adjust the\ncontents\", but my example above shows the same is true if directory\nrenaming is turned off.  The person doing the merge needs to somehow\nknow there may have been a directory rename and possibly move files\nand update references or things may have semantic breaks.  This seems\nno different to me than normal content merges with e.g. new\ninvocations of a function added on one side of history and on the\nother side of history new parameters added to the function call\nsignature.  Actually, I guess there is one difference: we have a way\nof detecting the (potential) directory rename and notifying the user\nof a possible change -- though we haven't taken advantage of this\npossibility yet (handle_rename_via_dir() silently moves files).\n\nPerhaps, though, you see directory rename heuristics as somehow\nriskier than semantic merge conflicts of other types, and perhaps we\nshould add more options here.  But I think there are other possible\nsolutions here which are worthy of consideration before choosing which\nbehavior we want.  Thinking just in terms of the default behavior,\nhere are eight possibilities:\n  * leave the capability on with no changes (no ability to opt-out,\nother than turning rename detection off entirely)\n  * remove the directory rename detection capability entirely (go back\nto pre- git-2.18 behavior)\n  * opt-out (let users choose pre-git-2.18 behavior, but default to\nsilently moving files according to directory rename heuristics)\n  * opt-in (directory rename detection is off by default and users are\ngiven no notice that paths should perhaps be renamed)\n  * display messages whenever directory rename detection adjusts a path location\n  * display messages whenever directory rename detection could adjust\na path location, but don't adjust it\n  * whenever directory rename detection adjusts a path location, mark\nthe new path as conflicted (i.e. record at either stage 2 or 3 instead\nof 0)\n  * whenever directory rename detection would adjust a path, leave it\nwhere it was but mark it as conflicted, and print a helpful message\n\nWhatever we choose for the default could be tweaked by some new option\n(e.g. make it less noisy or don't mark such paths as conflicted if the\nuser has explicitly stated their preference for or against directory\nrename detection).  I'm struggling to see directory rename detection\nas \"risky\" (though I certainly feel that lack of it is and thus do not\nlike the opt-in option), but if others feel there is risk here,\nwouldn't one of the last four options be safer than Peff's suggestion\nof opt-out or your suggestion of opt-in?\n\n\nThanks,\nElijah\n"},{"id":"370495","messageId":"xmqqimx03fo6.fsf@gitster-ct.c.googlers.com","threadId":"50603","inReplyTo":"CABPp-BFdbC2Shq8BrP=Ht21ZbjvEZVL_uQC0=3_YwbLJVbBU+g@mail.gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-03T01:33:29Z","receivedAt":"2019-03-03T01:33:35Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> Whatever we choose for the default could be tweaked by some new option\n> (e.g. make it less noisy or don't mark such paths as conflicted if the\n> user has explicitly stated their preference for or against directory\n> rename detection).  I'm struggling to see directory rename detection\n> as \"risky\" (though I certainly feel that lack of it is and thus do not\n> like the opt-in option), but if others feel there is risk here,\n\nI do not think it matters what \"others feel\" at all.\n\nThe simple fact that we are seeing a bug report that started this\nthread is enough to say that the heuristics kick in when the end\nusers do not expect to and when it happens it is harder to explain\nthan other kinds of rename detection heuristics.\n\n"},{"id":"370776","messageId":"20190306002744.14418-1-newren@gmail.com","threadId":"50603","inReplyTo":"xmqqimx03fo6.fsf@gitster-ct.c.googlers.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-06T00:27:44Z","receivedAt":"2019-03-06T00:28:18Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 2, 2019 at 5:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > Whatever we choose for the default could be tweaked by some new option\n> > (e.g. make it less noisy or don't mark such paths as conflicted if the\n> > user has explicitly stated their preference for or against directory\n> > rename detection).  I'm struggling to see directory rename detection\n> > as \"risky\" (though I certainly feel that lack of it is and thus do not\n> > like the opt-in option), but if others feel there is risk here,\n>\n> I do not think it matters what \"others feel\" at all.\n>\n> The simple fact that we are seeing a bug report that started this\n> thread is enough to say that the heuristics kick in when the end\n> users do not expect to and when it happens it is harder to explain\n> than other kinds of rename detection heuristics.\n>\n\nAlright, here is a patch to change the default, explaining the history\nand updating t6043 (directory rename tests) accordingly.\n\nThe following patch does not apply to master, due to building on\njk/unused-params; let me know if you want to see a backport.  I built\non jk/unused-params and partially reverted one of its patches (because\nnow there is a use for one of the parameters it removed), so that it\ncould apply cleanly to next and pu.\n\n\n-- 8< --\nSubject: [PATCH] merge-recursive: switch directory rename detection default\n\nWhen all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one\nbranch, there is a question about whether x/d added on a different\nbranch should remain at x/d or appear at z/d when the two branches are\nmerged.  There are different possible viewpoints here:\n\n  A) The file was placed at x/d; it's unrelated to the other files in\n     x/ so it doesn't matter that all the files from x/ moved to z/ on\n     one branch; x/d should still remain at x/d.\n\n  B) x/d is related to the other files in x/, and x/ was renamed to z/;\n     therefore x/d should be moved to z/d.\n\nSince there was no ability to detect directory renames prior to\ngit-2.18, users experienced (A) regardless of context.  Choice (B) was\nimplemented in git-2.18, with no option to go back to (A), and has been\nin use since.  However, one user reported that the merge results did not\nmatch their expectations, making the change of default problematic,\nespecially since there was no notice printed when directory rename\ndetection moved files.\n\nNote that there is also a third possibility here:\n\n  C) There are different answers depending on the context and content\n     that cannot be determined by git, so this is a conflict.  Use a\n     higher stage in the index to record the conflict and notify the\n     user of the potential issue instead of silently selecting a\n     resolution for them.\n\nAdd an option for users to specify their preference for whether to use\ndirectory rename detection, and default to (C).\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt      |  19 +++-\n merge-recursive.c                   |  83 +++++++++-----\n t/t3401-rebase-and-am-rename.sh     |   8 +-\n t/t6043-merge-rename-directories.sh | 171 +++++++++++++++++++---------\n 4 files changed, 195 insertions(+), 86 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex d389c73929..6a313937f8 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -39,9 +39,22 @@ merge.renameLimit::\n \tis turned off.\n \n merge.renames::\n-\tWhether and how Git detects renames.  If set to \"false\",\n-\trename detection is disabled. If set to \"true\", basic rename\n-\tdetection is enabled.  Defaults to the value of diff.renames.\n+\tWhether Git detects renames.  If set to \"false\", rename detection\n+\tis disabled. If set to \"true\", basic rename detection is enabled.\n+\tDefaults to the value of diff.renames.\n+\n+merge.directoryRenames::\n+\tWhether Git detects directory renames, affecting what happens at\n+\tmerge time to new files added to a directory on one side of\n+\thistory when that directory was renamed on the other side of\n+\thistory.  If merge.directoryRenames is set to \"false\", directory\n+\trename detection is disabled, meaning that such new files will be\n+\tleft behind in the old directory.  If set to \"true\", directory\n+\trename detection is enabled, meaning that such new files will be\n+\tmoved into the new directory.  If set to \"conflict\", a conflict\n+\twill be reported for such paths.  If merge.renames is false,\n+\tmerge.directoryRenames is ignored and treated as false.  Defaults\n+\tto \"conflict\".\n \n merge.renormalize::\n \tTell Git that canonical representation of files in the\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f270fa66f3..042548eeec 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1402,39 +1402,62 @@ static int merge_mode_and_contents(struct merge_options *o,\n \n static int handle_rename_via_dir(struct merge_options *o,\n \t\t\t\t struct diff_filepair *pair,\n-\t\t\t\t const char *rename_branch)\n+\t\t\t\t const char *rename_branch,\n+\t\t\t\t const char *other_branch)\n {\n \t/*\n \t * Handle file adds that need to be renamed due to directory rename\n-\t * detection.  This differs from handle_rename_normal, because\n-\t * there is no content merge to do; just move the file into the\n-\t * desired final location.\n+\t * detection, based on the setting of o->detect_directory_renames:\n+\t *   o->detect_directory_renames == 0: never use directory renames\n+\t *   o->detect_directory_renames == 1: report directory conflict\n+\t *   o->detect_directory_renames == 2: always use directory renames\n+\t * However, this function will not even be called when the value is\n+\t * 0, so we only have to worry about values of 1 and 2.\n+\t *\n+\t * This function differs from handle_rename_normal, because there\n+\t * is no content merge to do; we can just mark the file as\n+\t * conflicted and/or move the file into the desired final location.\n \t */\n \tconst struct diff_filespec *dest = pair->two;\n-\n+\tchar *file_path = dest->path;\n+\tint mark_conflicted = (o->detect_directory_renames == 1);\n+\n+\tif (mark_conflicted)\n+\t\toutput(o, 1, _(\"CONFLICT (directory possibly renamed): %s \"\n+\t\t\t       \"added in %s, but that directory was renamed \"\n+\t\t\t       \"in %s suggesting it should perhaps be moved \"\n+\t\t\t       \"to %s.\"),\n+\t\t       pair->one->path, other_branch, rename_branch,\n+\t\t       dest->path);\n \tif (!o->call_depth && would_lose_untracked(o, dest->path)) {\n-\t\tchar *alt_path = unique_path(o, dest->path, rename_branch);\n-\n+\t\tmark_conflicted = 1;\n+\t\tfile_path = unique_path(o, dest->path, rename_branch);\n \t\toutput(o, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n \t\t\t       \"writing to %s instead.\"),\n-\t\t       dest->path, alt_path);\n+\t\t       dest->path, file_path);\n+\t}\n+\n+\tif (mark_conflicted) {\n \t\t/*\n-\t\t * Write the file in worktree at alt_path, but not in the\n-\t\t * index.  Instead, write to dest->path for the index but\n-\t\t * only at the higher appropriate stage.\n+\t\t * Write the file in worktree at file_path.  In the index,\n+\t\t * only record the file at dest->path in the appropriate\n+\t\t * higher stage.\n \t\t */\n-\t\tif (update_file(o, 0, &dest->oid, dest->mode, alt_path))\n+\t\tif (update_file(o, 0, &dest->oid, dest->mode, file_path))\n \t\t\treturn -1;\n-\t\tfree(alt_path);\n-\t\treturn update_stages(o, dest->path, NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n+\t\tif (update_stages(o, dest->path, NULL,\n+\t\t\t\t  rename_branch == o->branch1 ? dest : NULL,\n+\t\t\t\t  rename_branch == o->branch1 ? NULL : dest))\n+\t\t\treturn -1;\n+\t\tif (file_path != dest->path)\n+\t\t\tfree(file_path);\n+\t\treturn 0; /* not clean, but conflicted */\n+\t} else {\n+\t\t/* Update dest->path both in index and in worktree */\n+\t\tif (update_file(o, 1, &dest->oid, dest->mode, dest->path))\n+\t\t\treturn -1;\n+\t\treturn 1; /* clean */\n \t}\n-\n-\t/* Update dest->path both in index and in worktree */\n-\tif (update_file(o, 1, &dest->oid, dest->mode, dest->path))\n-\t\treturn -1;\n-\treturn 0;\n }\n \n static int handle_change_delete(struct merge_options *o,\n@@ -3261,11 +3284,10 @@ static int process_entry(struct merge_options *o,\n \t\t\t\t\t\t\t   conflict_info);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n-\t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(o,\n-\t\t\t\t\t\t  conflict_info->pair1,\n-\t\t\t\t\t\t  conflict_info->branch1))\n-\t\t\t\tclean_merge = -1;\n+\t\t\tclean_merge = handle_rename_via_dir(o,\n+\t\t\t\t\t\t\t    conflict_info->pair1,\n+\t\t\t\t\t\t\t    conflict_info->branch1,\n+\t\t\t\t\t\t\t    conflict_info->branch2);\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n \t\t\t/*\n@@ -3681,6 +3703,15 @@ static void merge_recursive_config(struct merge_options *o)\n \t\to->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n \t\tfree(value);\n \t}\n+\tif (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n+\t\tif (!strcasecmp(value, \"true\"))\n+\t\t\to->detect_directory_renames = 2;\n+\t\tif (!strcasecmp(value, \"false\"))\n+\t\t\to->detect_directory_renames = 0;\n+\t\tif (!strcasecmp(value, \"conflict\"))\n+\t\t\to->detect_directory_renames = 1;\n+\t\tfree(value);\n+\t}\n \tgit_config(git_xmerge_config, NULL);\n }\n \ndiff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh\nindex e0b5111993..a0b9438b22 100755\n--- a/t/t3401-rebase-and-am-rename.sh\n+++ b/t/t3401-rebase-and-am-rename.sh\n@@ -42,7 +42,7 @@ test_expect_success 'rebase --interactive: directory rename detected' '\n \t\tgit checkout B^0 &&\n \n \t\tset_fake_editor &&\n-\t\tFAKE_LINES=\"1\" git rebase --interactive A &&\n+\t\tFAKE_LINES=\"1\" git -c merge.directoryRenames=true rebase --interactive A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -58,7 +58,7 @@ test_expect_failure 'rebase (am): directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase A &&\n+\t\tgit -c merge.directoryRenames=true rebase A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -74,7 +74,7 @@ test_expect_success 'rebase --merge: directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase --merge A &&\n+\t\tgit -c merge.directoryRenames=true rebase --merge A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -92,7 +92,7 @@ test_expect_failure 'am: directory rename detected' '\n \n \t\tgit format-patch -1 B &&\n \n-\t\tgit am --3way 0001*.patch &&\n+\t\tgit -c merge.directoryRenames=true am --3way 0001*.patch &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\ndiff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh\nindex 62c564707b..1048ec2cac 100755\n--- a/t/t6043-merge-rename-directories.sh\n+++ b/t/t6043-merge-rename-directories.sh\n@@ -75,7 +75,7 @@ test_expect_success '1a-check: Simple directory rename detection' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -142,7 +142,7 @@ test_expect_success '1b-check: Merge a directory with another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -201,7 +201,7 @@ test_expect_success '1c-check: Transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -270,7 +270,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -350,7 +350,7 @@ test_expect_success '1e-check: Renamed directory, with all files being renamed t\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -416,7 +416,7 @@ test_expect_success '1f-check: Split a directory into two other directories' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -497,7 +497,7 @@ test_expect_success '2a-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*directory rename split\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -559,7 +559,7 @@ test_expect_success '2b-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -640,7 +640,7 @@ test_expect_success '3a-check: Avoid implicit rename if involved as source on ot\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -705,7 +705,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&\n \t\ttest_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&\n \n@@ -826,7 +826,7 @@ test_expect_success '4a-check: Directory split, with original directory still pr\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -915,7 +915,7 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*implicit dir rename\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -989,7 +989,7 @@ test_expect_success '5b-check: Rename/delete in order to get add/add/add conflic\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1069,7 +1069,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*z/d\" out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n@@ -1153,7 +1153,7 @@ test_expect_success '5d-check: Directory/file/file conflict due to directory ren\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (file/directory).*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1243,7 +1243,7 @@ test_expect_success '6a-check: Tricky rename/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*z/c.*y/c\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1308,7 +1308,7 @@ test_expect_success '6b-check: Same rename done on both sides' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1370,7 +1370,7 @@ test_expect_success '6c-check: Rename only done on same side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1432,7 +1432,7 @@ test_expect_success '6d-check: We do not always want transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1495,7 +1495,7 @@ test_expect_success '6e-check: Add/add from one side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -1591,7 +1591,7 @@ test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/b.*y/b.*w/b\" out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/c.*y/c.*x/c\" out &&\n \n@@ -1663,7 +1663,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1740,7 +1740,7 @@ test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1804,7 +1804,7 @@ test_expect_success '7d-check: transitive rename involved in rename/delete; how\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1894,7 +1894,7 @@ test_expect_success '7e-check: transitive rename in rename/delete AND dirs in th\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1985,7 +1985,7 @@ test_expect_success '8a-check: Dual-directory rename, one into the others way' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2063,7 +2063,7 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2135,7 +2135,7 @@ test_expect_success '8c-check: modify/delete or rename+modify/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (modify/delete).* z/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2212,7 +2212,7 @@ test_expect_success '8d-check: rename/delete...or not?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2287,7 +2287,7 @@ test_expect_success '8e-check: Both sides rename, one side adds to original dire\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&\n \n@@ -2374,7 +2374,7 @@ test_expect_success '9a-check: Inner renamed directory within outer renamed dire\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 7 out &&\n@@ -2444,7 +2444,7 @@ test_expect_success '9b-check: Transitive rename with content merge' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2534,7 +2534,7 @@ test_expect_success '9c-check: Doubly transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> z rename to x/f\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2622,7 +2622,7 @@ test_expect_success '9d-check: N-way transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying z -> y rename to z/t\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying y -> x rename to y/a\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> w rename to x/b\" out &&\n@@ -2704,7 +2704,7 @@ test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\tgrep \"CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo\" out >error_line &&\n \t\tgrep -q dir1/yo error_line &&\n \t\tgrep -q dir2/yo error_line &&\n@@ -2782,7 +2782,7 @@ test_expect_success '9f-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2849,7 +2849,7 @@ test_expect_failure '9g-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2918,7 +2918,7 @@ test_expect_success '9h-check: Avoid dir rename on merely modified path' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2993,7 +2993,7 @@ test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '\n \t\techo very >z/c &&\n \t\techo important >z/d &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"The following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3061,7 +3061,7 @@ test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '\n \t\techo important >y/d &&\n \t\techo contents >y/e &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*Version B\\^0 of y/d left in tree at y/d~B\\^0\" out &&\n \t\ttest_i18ngrep \"Error: Refusing to lose untracked file at y/e; writing to y/e~B\\^0 instead\" out &&\n \n@@ -3137,7 +3137,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tgit checkout A^0 &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~B\\^0 instead\" out &&\n \n@@ -3174,7 +3174,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tmkdir y &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive A^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~HEAD instead\" out &&\n \n@@ -3249,7 +3249,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '\n \t\tgit checkout A^0 &&\n \t\techo important >y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/wham\" out &&\n \n@@ -3327,7 +3327,7 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n\n \t\tmkdir z &&\n \t\techo random >z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep ! \"following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3407,7 +3407,7 @@ test_expect_success '11a-check: Avoid losing dirty contents with simple rename'\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\ttest_seq 1 10 >expected &&\n@@ -3479,7 +3479,7 @@ test_expect_success '11b-check: Avoid losing dirty file involved in directory re\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3554,7 +3554,7 @@ test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"following files would be overwritten by merge\" err &&\n \n \t\tgrep -q stuff y/c &&\n@@ -3621,7 +3621,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3700,7 +3700,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo mods >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/c\" out &&\n \n@@ -3782,7 +3782,7 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo important >>y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/wham\" out &&\n \n@@ -3870,7 +3870,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -3946,7 +3946,7 @@ test_expect_success '12b-check: Moving one directory hierarchy into another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -4016,7 +4016,7 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -u >out &&\n \t\ttest_line_count = 12 out &&\n@@ -4051,4 +4051,69 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \t)\n '\n \n+# Testcase 12d, Basic directory rename with default \"conflict\" setting\n+#   Commit O: z/{b,c}\n+#   Commit A: y/{b,c}\n+#   Commit B: z/{b,c,d,e/f}\n+#   Expected: y/{b,c,d,e/f}, with both y/d and y/e/f marked as conflicted\n+\n+test_expect_success '12d-setup: Conflict handling for default setting' '\n+\ttest_create_repo 12d &&\n+\t(\n+\t\tcd 12d &&\n+\n+\t\tmkdir z &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo d >z/d &&\n+\t\tmkdir z/e &&\n+\t\techo f >z/e/f &&\n+\t\tgit add z/d z/e/f &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '12d-check: Conflict handling for default setting' '\n+\t(\n+\t\tcd 12d &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 &&\n+\n+\t\tgit ls-files -s >out &&\n+\t\ttest_line_count = 4 out &&\n+\n+\t\tgit rev-parse >actual \\\n+\t\t\tHEAD:y/b HEAD:y/c :3:y/d :3:y/e/f &&\n+\t\tgit rev-parse >expect \\\n+\t\t\tO:z/b    O:z/c    B:z/d    B:z/e/f &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\tgit hash-object y/d >actual &&\n+\t\tgit rev-parse B:z/d >expect &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\ttest_must_fail git rev-parse HEAD:z/d &&\n+\t\ttest_must_fail git rev-parse HEAD:z/e/f &&\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_missing z/e/f\n+\t)\n+'\n+\n test_done\n-- \n2.21.0.rc1.19.g5671d8e416\n\n"},{"id":"370781","messageId":"xmqqh8cgr4tf.fsf@gitster-ct.c.googlers.com","threadId":"50603","inReplyTo":"20190306002744.14418-1-newren@gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-06T04:43:08Z","receivedAt":"2019-03-06T04:43:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> Note that there is also a third possibility here:\n>\n>   C) There are different answers depending on the context and content\n>      that cannot be determined by git, so this is a conflict.  Use a\n>      higher stage in the index to record the conflict and notify the\n>      user of the potential issue instead of silently selecting a\n>      resolution for them.\n>\n> Add an option for users to specify their preference for whether to use\n> directory rename detection, and default to (C).\n\nYeah, (c) is the sanest default that can give the best of both\nworlds, I would have to say.\n\nSometimes the heuristics guess that the new one may want to go to\nthe same place as all the neighbours, and other times the new one\nmay want to stay.  By using stage #2 and stage #3 appropriately, the\nuser can see where the final result wants to be in.  Because by\ndefinition there won't be any auto-resolvable content-level merge\nfor such paths (after all, one side is adding it as a new file), I\nthink the contents for these two higher-stage entries would actually\nbe the same?\n\n> +\tif (mark_conflicted)\n> +\t\toutput(o, 1, _(\"CONFLICT (directory possibly renamed): %s \"\n> +\t\t\t       \"added in %s, but that directory was renamed \"\n> +\t\t\t       \"in %s suggesting it should perhaps be moved \"\n> +\t\t\t       \"to %s.\"),\n> +\t\t       pair->one->path, other_branch, rename_branch,\n> +\t\t       dest->path);\n\nWhat confused the end user is that it is not always the case, and\nthat is because the only thing we know at this point is that the\nother branch moved all the files in that directory in their branch\nto the other place, not that they \"moved the directory\".\n\nIn other words, if \"that directory was renamed\" in this message were\ndefinitive and agreed with end-user perception, we won't be issuing\nthis conflict with a label that says \"possibly\" renamed.\n\nPerhaps something along this line?\n\n\t%(pair->one->path)s added in %(other_branch)s, but the other\n\tbranch %(rename_branch)s moved all the files in the same\n\tdirectory to %(dirname(dest->path))s, so this file may also\n\twant to move there, too.\n\n>  \tif (!o->call_depth && would_lose_untracked(o, dest->path)) {\n> -\t\tchar *alt_path = unique_path(o, dest->path, rename_branch);\n> -\n> +\t\tmark_conflicted = 1;\n> +\t\tfile_path = unique_path(o, dest->path, rename_branch);\n>  \t\toutput(o, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n>  \t\t\t       \"writing to %s instead.\"),\n> -\t\t       dest->path, alt_path);\n> +\t\t       dest->path, file_path);\n> +\t}\n> +\n> +\tif (mark_conflicted) {\n>  \t\t/*\n> -\t\t * Write the file in worktree at alt_path, but not in the\n> -\t\t * index.  Instead, write to dest->path for the index but\n> -\t\t * only at the higher appropriate stage.\n> +\t\t * Write the file in worktree at file_path.  In the index,\n> +\t\t * only record the file at dest->path in the appropriate\n> +\t\t * higher stage.\n>  \t\t */\n> -\t\tif (update_file(o, 0, &dest->oid, dest->mode, alt_path))\n> +\t\tif (update_file(o, 0, &dest->oid, dest->mode, file_path))\n>  \t\t\treturn -1;\n> +\t\tif (update_stages(o, dest->path, NULL,\n> +\t\t\t\t  rename_branch == o->branch1 ? dest : NULL,\n> +\t\t\t\t  rename_branch == o->branch1 ? NULL : dest))\n> +\t\t\treturn -1;\n\nThis one does not issue \"may want to stay here\" comment of its own,\nso the earlier \"CONFLICT (directory possibly renamed)\" message would\nneed to cover both of these two higher-stage entries.  I think the\nmessage mentions the two paths, so it would be good already.\n\nThe earlier \"CONFLICT (directory possibly renamed)\" message may be\nsufficient to explain the situation, but I wonder if the user would\nneed hints in resolving either of the two possible ways.  I do not\nknow if it should be in the same message, or in the manpage update,\nbut first let's make sure what we want the end users to understand.\nIIUC, the hint for resolving in favor of either would be to do these\ntwo commands?\n\n\tgit checkout --ours $path_the_user_wants_to_use\n\tgit rm --cached $the_other_path\n\nIf the path the user wants to use matches what the branch being\nmerged has, then replace \"--ours\" above with \"--theirs\".\n\nWould that be a good explanation?\n\nRegarding the implementation (not the use of stages, but how\n\"mark_conflicted\" variable gets used), I am not sure if I agree that\nonly choice (c) should give the hint.  Regardless of the settings,\nwe know both paths this file may want to end up with, and I suspect\nthat the alternative a/b/c you gave are how by default the tool\nautomatically places the result, giving a chance to the end user to\ncorrect mistakes.  I wonder if it is possible to somehow show \"the\nother possibilty\" in the merge log even when choice (a) or (b) are\nin effect?  E.g. \"path X/B moved to Y/B because the other side moved\nX/A to Y/A\" when choice (b) is in use, and \"path X/B kept there but\nthe other side moved neighbouring A/X to Y/A\" when choice (a) is in\nuse.\n\nAnother thing that makes me wonder is if this should be treated\nsimilar to rerere.autoupdate; even when rerere kicks in to resolve\notherwise conflicting tricky merge, without being explicitly allowed\nwith the config, it does not register the resolution to the index at\nstage #0.  Either choices (a) that ignores neighbouring files'\nmovement or (b) that follows the movement lets this tricky\nheuristics not just suggest the end result, but allows it to\nregister the result at stage #0, which somehow feels too aggressive\n(which was what triggered my suggestion to go with (c)).  With that\nattitude, we may be able to get rid of choice (a) altogether, which\nmay make things a tad simpler.  IOW, we assume the heuristics would\nsuggest the right solution most of the time, just like we assume\nrerere gives a reasonable resolution most of the time, with a knob\nto accept the result blindly, together with hints to recover when\nheuristics makes mistakes.\n\n"},{"id":"370860","messageId":"CABPp-BF=GFgFRfgdG2rFK89x1tYrJO8TpECyv6BoTgz9q3Rb+g@mail.gmail.com","threadId":"50603","inReplyTo":"xmqqh8cgr4tf.fsf@gitster-ct.c.googlers.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-07T04:14:37Z","receivedAt":"2019-03-07T04:14:52Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 5, 2019 at 8:43 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Elijah Newren <newren@gmail.com> writes:\n>\n> > Note that there is also a third possibility here:\n> >\n> >   C) There are different answers depending on the context and content\n> >      that cannot be determined by git, so this is a conflict.  Use a\n> >      higher stage in the index to record the conflict and notify the\n> >      user of the potential issue instead of silently selecting a\n> >      resolution for them.\n> >\n> > Add an option for users to specify their preference for whether to use\n> > directory rename detection, and default to (C).\n>\n> Yeah, (c) is the sanest default that can give the best of both\n> worlds, I would have to say.\n\nGreat, sounds like we're in agreement on the high level objective.  :-)\n\n> Sometimes the heuristics guess that the new one may want to go to\n> the same place as all the neighbours, and other times the new one\n> may want to stay.  By using stage #2 and stage #3 appropriately, the\n> user can see where the final result wants to be in.  Because by\n> definition there won't be any auto-resolvable content-level merge\n> for such paths (after all, one side is adding it as a new file), I\n> think the contents for these two higher-stage entries would actually\n> be the same?\n\nI'm not sure I'm following well enough to answer any your question(s),\nso I'll instead ask a few of my own:\n  * Are you referring to stage #2 and stage #3 of the old path or the\nnew path or one of each or both of each?\n  * Would this be consistent with how normal renames behave?\nand, as a partial answer to your question:\n  * Does that also work with files renamed into the old directory\ninstead of just added?\n\nI don't know if normal renames are similar enough to the directory\nrename case in this particular area that it makes sense to compare,\nbut there was one specific thing that came to my mind that I wondered\nabout:  For normal renames, we only have index entries for the new\npath rather than the old path even if there are content level merge\nconflicts.  So, for example if a 'list_of_favorites.txt' was modified\non both sides of history and one side renames to\n$SUBDIR/list_of_favorites.txt, then instead of having any entries in\nthe index for list_of_favorites.txt, we have three entries for\n$SUBDIR/list_of_favorites.txt.  That makes it easier for users to\nresolve (they can use a single git add and no git rm).\n\nNote that as a special case for this rename, we could have $SUBDIR/ =\nx/, because if the other side renamed x/ -> z/, then\nlist_of_favorites.txt should perhaps be transitively renamed to\nz/list_of_favorites.txt.  Although this kind of thing is more rare\nthan just adding files to the old directory, this kind of case comes\nup too, so there actually could be content-level merge for paths\naffected by directory rename detection.  (Sidenote more for myself:\nTransitively renamed paths feed through handle_rename_normal() instead\nof handle_rename_via_dir(), so I need to add the new conflict/warning\nmessages to handle_rename_normal() too.)\n\n> > +     if (mark_conflicted)\n> > +             output(o, 1, _(\"CONFLICT (directory possibly renamed): %s \"\n> > +                            \"added in %s, but that directory was renamed \"\n> > +                            \"in %s suggesting it should perhaps be moved \"\n> > +                            \"to %s.\"),\n> > +                    pair->one->path, other_branch, rename_branch,\n> > +                    dest->path);\n>\n> What confused the end user is that it is not always the case, and\n> that is because the only thing we know at this point is that the\n> other branch moved all the files in that directory in their branch\n> to the other place, not that they \"moved the directory\".\n>\n> In other words, if \"that directory was renamed\" in this message were\n> definitive and agreed with end-user perception, we won't be issuing\n> this conflict with a label that says \"possibly\" renamed.\n>\n> Perhaps something along this line?\n>\n>         %(pair->one->path)s added in %(other_branch)s, but the other\n>         branch %(rename_branch)s moved all the files in the same\n>         directory to %(dirname(dest->path))s, so this file may also\n>         want to move there, too.\n\nThat sounds better, thanks.  A follow-up question: We normally have\nconflict messages of the form:\n    CONFLICT(<summary>): <long description>\nYou've provided an improved long description, but do you have a\nsuggestion for the summary?  \"path location\"?  \"directory possibly\nrenamed\"?  Something else?\n\n> >       if (!o->call_depth && would_lose_untracked(o, dest->path)) {\n> > -             char *alt_path = unique_path(o, dest->path, rename_branch);\n> > -\n> > +             mark_conflicted = 1;\n> > +             file_path = unique_path(o, dest->path, rename_branch);\n> >               output(o, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n> >                              \"writing to %s instead.\"),\n> > -                    dest->path, alt_path);\n> > +                    dest->path, file_path);\n> > +     }\n> > +\n> > +     if (mark_conflicted) {\n> >               /*\n> > -              * Write the file in worktree at alt_path, but not in the\n> > -              * index.  Instead, write to dest->path for the index but\n> > -              * only at the higher appropriate stage.\n> > +              * Write the file in worktree at file_path.  In the index,\n> > +              * only record the file at dest->path in the appropriate\n> > +              * higher stage.\n> >                */\n> > -             if (update_file(o, 0, &dest->oid, dest->mode, alt_path))\n> > +             if (update_file(o, 0, &dest->oid, dest->mode, file_path))\n> >                       return -1;\n> > +             if (update_stages(o, dest->path, NULL,\n> > +                               rename_branch == o->branch1 ? dest : NULL,\n> > +                               rename_branch == o->branch1 ? NULL : dest))\n> > +                     return -1;\n>\n> This one does not issue \"may want to stay here\" comment of its own,\n> so the earlier \"CONFLICT (directory possibly renamed)\" message would\n> need to cover both of these two higher-stage entries.  I think the\n> message mentions the two paths, so it would be good already.\n>\n> The earlier \"CONFLICT (directory possibly renamed)\" message may be\n> sufficient to explain the situation, but I wonder if the user would\n> need hints in resolving either of the two possible ways.  I do not\n> know if it should be in the same message, or in the manpage update,\n> but first let's make sure what we want the end users to understand.\n> IIUC, the hint for resolving in favor of either would be to do these\n> two commands?\n>\n>         git checkout --ours $path_the_user_wants_to_use\n>         git rm --cached $the_other_path\n>\n> If the path the user wants to use matches what the branch being\n> merged has, then replace \"--ours\" above with \"--theirs\".\n>\n> Would that be a good explanation?\n\nOh, good point; we may need additional documentation to address this,\nthough it would be nice if we could minimize it.  As far as the\nparticular solution, I think we should avoid checkout --ours, for a\nfew reasons:\n  * 'checkout --ours' only reads from and does not update the index,\nso it wouldn't resolve the conflict.\n  * I think expecting users to figure out --ours vs. --theirs is\nconfusing, given that users perceive them as reversed in rebase vs\nmerge, and cherry-pick is somewhere inbetween.\n  * In the case of a transitively renamed path (like\nlist_of_favorites.txt above), --ours might ignore content changes from\none side.\n\nI would prefer to tell the user they can resolve the conflict with two\nother commands.  I'm currently thinking of something like these two:\n    git add $directory_renamed_path\n    git mv $directory_renamed_path $original_path [&& git add $original_path ?]\nThis pair assumes we write the working tree and index entries for\n$directory_renamed_path and leave $original_path empty in both the\nworking tree and index.  We could alternatively reverse that, with an\nequivalent but swapped set of commands for resolution. We can't use\nthis exact set of commands today, as users would have to do a 'git\nadd' before a 'git mv'.  I think 'git mv' should be modified to allow\nworking with higher stage entries (possibly always preserving them?),\nwhich has come up before[1].\n\n[1] https://public-inbox.org/git/xmqqbmabcuhf.fsf@gitster-ct.c.googlers.com/\n\n> Regarding the implementation (not the use of stages, but how\n> \"mark_conflicted\" variable gets used), I am not sure if I agree that\n> only choice (c) should give the hint.  Regardless of the settings,\n> we know both paths this file may want to end up with, and I suspect\n> that the alternative a/b/c you gave are how by default the tool\n> automatically places the result, giving a chance to the end user to\n> correct mistakes.  I wonder if it is possible to somehow show \"the\n> other possibilty\" in the merge log even when choice (a) or (b) are\n> in effect?  E.g. \"path X/B moved to Y/B because the other side moved\n> X/A to Y/A\" when choice (b) is in use, and \"path X/B kept there but\n> the other side moved neighbouring A/X to Y/A\" when choice (a) is in\n> use.\n\nI think it makes sense when (b) is in effect but I'm not sure about\n(a).  To print such messages, we first have to try to detect renames\n(which we currently don't do with (a) though we could change that),\nand for the messages to be useful we should believe they are better\nthan a coin flip's chance of being right.  We use choice (a) with\ngit-am right now because the fake-ancestor-tree thing means we believe\nany directory rename detection is likely to be wrong.\n\n> Another thing that makes me wonder is if this should be treated\n> similar to rerere.autoupdate; even when rerere kicks in to resolve\n> otherwise conflicting tricky merge, without being explicitly allowed\n> with the config, it does not register the resolution to the index at\n> stage #0.  Either choices (a) that ignores neighbouring files'\n> movement or (b) that follows the movement lets this tricky\n> heuristics not just suggest the end result, but allows it to\n> register the result at stage #0, which somehow feels too aggressive\n> (which was what triggered my suggestion to go with (c)).  With that\n> attitude, we may be able to get rid of choice (a) altogether, which\n> may make things a tad simpler.  IOW, we assume the heuristics would\n> suggest the right solution most of the time, just like we assume\n> rerere gives a reasonable resolution most of the time, with a knob\n> to accept the result blindly, together with hints to recover when\n> heuristics makes mistakes.\n\nI like that analogy.  I think that aligns well with my possible\nconflict-resolution commands, and further answers the question about\nwhether to put the new path or the old path in the working tree and\nindex.\n\nHowever, we do need some form of choice (a) for commands which can't\nprovide accurate tree information, such as git-am.\n"},{"id":"370862","messageId":"xmqqzhq7me4y.fsf@gitster-ct.c.googlers.com","threadId":"50603","inReplyTo":"CABPp-BF=GFgFRfgdG2rFK89x1tYrJO8TpECyv6BoTgz9q3Rb+g@mail.gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-07T05:45:17Z","receivedAt":"2019-03-07T05:45:24Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> I'm not sure I'm following well enough to answer any your question(s),\n> so I'll instead ask a few of my own:\n>   * Are you referring to stage #2 and stage #3 of the old path or the\n> new path or one of each or both of each?\n\nIf they moved the sole neighbouring file X/A to Y/A, while we added\na new path to X/B, I would expect we'd have two blobs with the same\ncontent we added, one at X/B at stage #2 (because that is what we\nwanted to have) and the other at Y/B at stage #3 (that's their wish\nthe heuristic is guessing).  If it were the other way, the stages\nwould be different.\n\n>   * Would this be consistent with how normal renames behave?\n> and, as a partial answer to your question:\n\nThe use of the three-way merge logic at the tree structure level,\nunlike the \"because neighbours moved\" heuristics, gives a more\ndefinite answer.  We see that the contents in the original at path A\n(in the common ancestor version) is now at path B (with or without\nslight modification) in their tree, while we kept it at path A\n(again, with or without modification).  Or vice versa.  Either way,\nat the tree structure level, that is \"one side changes, while the\nother side does not modify\" which is resolved cleanly to \"take the\nnew path the side that modifies wants the file to have\".  So we do\nnot need to use the stages to represent \"these sides wants to place\nit at different paths, which one is correct?\"  Instead, we'd have\nstages #1 (common ancestor), #2 (ours) and #3 (theirs) all at path B\n(i.e. the cleanly resolved structural change conflict) to help the\nuser reconcile content level conflicts (iff needed).\n\n\n> I would prefer to tell the user they can resolve the conflict with two\n> other commands.  I'm currently thinking of something like these two:\n>     git add $directory_renamed_path\n>     git mv $directory_renamed_path $original_path [&& git add $original_path ?]\n> This pair assumes we write the working tree and index entries for\n> $directory_renamed_path and leave $original_path empty in both the\n> working tree and index.  We could alternatively reverse that, with an\n> equivalent but swapped set of commands for resolution.\n\nYeah, the final version may have difference in detail, but I think\nit is sensible to leave a state, from which the user can go in\neither direction, with a simple command or two, like the above one.\n\n>> ...  With that\n>> attitude, we may be able to get rid of choice (a) altogether, which\n>> may make things a tad simpler.  IOW, we assume the heuristics would\n>> suggest the right solution most of the time, just like we assume\n>> rerere gives a reasonable resolution most of the time, with a knob\n>> to accept the result blindly, together with hints to recover when\n>> heuristics makes mistakes.\n>\n> I like that analogy.  I think that aligns well with my possible\n> conflict-resolution commands, and further answers the question about\n> whether to put the new path or the old path in the working tree and\n> index.\n>\n> However, we do need some form of choice (a) for commands which can't\n> provide accurate tree information, such as git-am.\n\nYeah, if we are not even running the heuristics for that case (a),\nhence it is not sensible to expect to offer \"if we were doing\ndirectory renames, this path might have gone to that other place\",\nthen I think I am fine with not giving any hint to change their mind\n(after all, they can \"git reset --hard\" and retry the merge, this\ntime not using the non-default (a) mode).  And keeping around the\nsimplest \"we do not even attempt to\" (a) mode as another option is\nfine by me.\n\nTHanks.\n"},{"id":"370863","messageId":"xmqqy35rme4q.fsf@gitster-ct.c.googlers.com","threadId":"50603","inReplyTo":"CABPp-BF=GFgFRfgdG2rFK89x1tYrJO8TpECyv6BoTgz9q3Rb+g@mail.gmail.com","subject":"Re: [BUG] All files in folder are moved when cherry-picking commit that moves fewer files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-07T05:45:25Z","receivedAt":"2019-03-07T05:45:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> I'm not sure I'm following well enough to answer any your question(s),\n> so I'll instead ask a few of my own:\n>   * Are you referring to stage #2 and stage #3 of the old path or the\n> new path or one of each or both of each?\n\nIf they moved the sole neighbouring file X/A to Y/A, while we added\na new path to X/B, I would expect we'd have two blobs with the same\ncontent we added, one at X/B at stage #2 (because that is what we\nwanted to have) and the other at Y/B at stage #3 (that's their wish\nthe heuristic is guessing).  If it were the other way, the stages\nwould be different.\n\n>   * Would this be consistent with how normal renames behave?\n> and, as a partial answer to your question:\n\nThe use of the three-way merge logic at the tree structure level,\nunlike the \"because neighbours moved\" heuristics, gives a more\ndefinite answer.  We see that the contents in the original at path A\n(in the common ancestor version) is now at path B (with or without\nslight modification) in their tree, while we kept it at path A\n(again, with or without modification).  Or vice versa.  Either way,\nat the tree structure level, that is \"one side changes, while the\nother side does not modify\" which is resolved cleanly to \"take the\nnew path the side that modifies wants the file to have\".  So we do\nnot need to use the stages to represent \"these sides wants to place\nit at different paths, which one is correct?\"  Instead, we'd have\nstages #1 (common ancestor), #2 (ours) and #3 (theirs) all at path B\n(i.e. the cleanly resolved structural change conflict) to help the\nuser reconcile content level conflicts (iff needed).\n\n\n> I would prefer to tell the user they can resolve the conflict with two\n> other commands.  I'm currently thinking of something like these two:\n>     git add $directory_renamed_path\n>     git mv $directory_renamed_path $original_path [&& git add $original_path ?]\n> This pair assumes we write the working tree and index entries for\n> $directory_renamed_path and leave $original_path empty in both the\n> working tree and index.  We could alternatively reverse that, with an\n> equivalent but swapped set of commands for resolution.\n\nYeah, the final version may have difference in detail, but I think\nit is sensible to leave a state, from which the user can go in\neither direction, with a simple command or two, like the above one.\n\n>> ...  With that\n>> attitude, we may be able to get rid of choice (a) altogether, which\n>> may make things a tad simpler.  IOW, we assume the heuristics would\n>> suggest the right solution most of the time, just like we assume\n>> rerere gives a reasonable resolution most of the time, with a knob\n>> to accept the result blindly, together with hints to recover when\n>> heuristics makes mistakes.\n>\n> I like that analogy.  I think that aligns well with my possible\n> conflict-resolution commands, and further answers the question about\n> whether to put the new path or the old path in the working tree and\n> index.\n>\n> However, we do need some form of choice (a) for commands which can't\n> provide accurate tree information, such as git-am.\n\nYeah, if we are not even running the heuristics for that case (a),\nhence it is not sensible to expect to offer \"if we were doing\ndirectory renames, this path might have gone to that other place\",\nthen I think I am fine with not giving any hint to change their mind\n(after all, they can \"git reset --hard\" and retry the merge, this\ntime not using the non-default (a) mode).  And keeping around the\nsimplest \"we do not even attempt to\" (a) mode as another option is\nfine by me.\n\nThanks.\n"},{"id":"372742","messageId":"20190330003336.21940-9-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 08/15] merge-recursive: remove ren[12]_other fields from rename_conflict_info","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:29Z","receivedAt":"2019-03-30T00:34:17Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The ren1_other and ren2_other fields were synthesized from information\nin ren1->src_entry and ren2->src_entry.  Since we already have the\nnecessary information in ren1 and ren2, just use those.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 71 ++++++++++++++---------------------------------\n 1 file changed, 21 insertions(+), 50 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e05f8f22f5..e66b47cfa1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -242,8 +242,6 @@ struct rename_conflict_info {\n \tstruct rename *ren2;\n \tconst char *branch1;\n \tconst char *branch2;\n-\tstruct diff_filespec ren1_other;\n-\tstruct diff_filespec ren2_other;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n@@ -251,11 +249,8 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct rename *ren1,\n \t\t\t\t\t      struct rename *ren2,\n \t\t\t\t\t      const char *branch1,\n-\t\t\t\t\t      const char *branch2,\n-\t\t\t\t\t      struct stage_data *src_entry1,\n-\t\t\t\t\t      struct stage_data *src_entry2)\n+\t\t\t\t\t      const char *branch2)\n {\n-\tint ostage1 = 0, ostage2;\n \tstruct rename_conflict_info *ci;\n \n \t/*\n@@ -269,8 +264,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\tsetup_rename_conflict_info(rename_type,\n \t\t\t\t\t   opt,\n \t\t\t\t\t   ren2,       ren1,\n-\t\t\t\t\t   branch2,    branch1,\n-\t\t\t\t\t   src_entry2, src_entry1);\n+\t\t\t\t\t   branch2,    branch1);\n \t\treturn;\n \t}\n \n@@ -287,28 +281,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tif (ren2) {\n \t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n-\n-\t/*\n-\t * For each rename, there could have been\n-\t * modifications on the side of history where that\n-\t * file was not renamed.\n-\t */\n-\tif (rename_type == RENAME_ADD ||\n-\t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n-\n-\t\tci->ren1_other.path = ren1->pair->one->path;\n-\t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n-\t\tci->ren1_other.mode = src_entry1->stages[ostage1].mode;\n-\t}\n-\n-\tif (rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage2 = ostage1 ^ 1;\n-\n-\t\tci->ren2_other.path = ren2->pair->one->path;\n-\t\toidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);\n-\t\tci->ren2_other.mode = src_entry2->stages[ostage2].mode;\n-\t}\n }\n \n static int show(struct merge_options *opt, int v)\n@@ -1688,6 +1660,7 @@ static int handle_rename_add(struct merge_options *opt,\n \t/* a was renamed to c, and a separate c was added. */\n \tstruct diff_filespec *a = ci->ren1->pair->one;\n \tstruct diff_filespec *c = ci->ren1->pair->two;\n+\tstruct diff_filespec tmp;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1699,8 +1672,12 @@ static int handle_rename_add(struct merge_options *opt,\n \t       a->path, c->path, ci->branch1,\n \t       c->path, ci->branch2);\n \n+\tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n+\ttmp.path = a->path;\n+\n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(opt, a, c, &ci->ren1_other, prev_path_desc,\n+\tif (merge_mode_and_contents(opt, a, c, &tmp,\n+\t\t\t\t    prev_path_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -1850,6 +1827,7 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tstruct diff_filespec *b = ci->ren2->pair->one;\n \tstruct diff_filespec *c1 = ci->ren1->pair->two;\n \tstruct diff_filespec *c2 = ci->ren2->pair->two;\n+\tstruct diff_filespec tmp1, tmp2;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n@@ -1862,12 +1840,17 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t       a->path, c1->path, ci->branch1,\n \t       b->path, c2->path, ci->branch2);\n \n+\tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n+\ttmp1.path = a->path;\n+\tfilespec_from_entry(&tmp2, ci->ren2->src_entry, 2);\n+\ttmp2.path = b->path;\n+\n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(opt, a, c1, &ci->ren1_other, path_side_1_desc,\n+\tif (merge_mode_and_contents(opt, a, c1, &tmp1, path_side_1_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(opt, b, &ci->ren2_other, c2, path_side_2_desc,\n+\t    merge_mode_and_contents(opt, b, &tmp2, c2, path_side_2_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n@@ -2728,9 +2711,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t   branch2);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n \t\t\t/* Two different files renamed to the same thing */\n \t\t\tchar *ren2_dst;\n@@ -2753,9 +2734,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->src_entry,\n-\t\t\t\t\t\t   ren2->src_entry);\n+\t\t\t\t\t\t   branch2);\n \n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n@@ -2794,18 +2773,14 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n \t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n \t\t\t\t   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {\n \t\t\t\t/*\n@@ -2836,9 +2811,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->src_entry,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n@@ -2862,8 +2835,6 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n \t\t}\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372743","messageId":"20190330003336.21940-11-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 10/15] merge-recursive: cleanup handle_rename_* function signatures","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:31Z","receivedAt":"2019-03-30T00:34:18Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Instead of passing various bits and pieces of 'ci', just pass it\ndirectly.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 31 +++++++++++++++----------------\n 1 file changed, 15 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f85c276f35..ada1c19ed2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1357,8 +1357,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n }\n \n static int handle_rename_via_dir(struct merge_options *opt,\n-\t\t\t\t struct diff_filepair *pair,\n-\t\t\t\t const char *rename_branch)\n+\t\t\t\t struct rename_conflict_info *ci)\n {\n \t/*\n \t * Handle file adds that need to be renamed due to directory rename\n@@ -1366,10 +1365,11 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t * there is no content merge to do; just move the file into the\n \t * desired final location.\n \t */\n-\tconst struct diff_filespec *dest = pair->two;\n+\tconst struct rename *ren = ci->ren1;\n+\tconst struct diff_filespec *dest = ren->pair->two;\n \n \tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n-\t\tchar *alt_path = unique_path(opt, dest->path, rename_branch);\n+\t\tchar *alt_path = unique_path(opt, dest->path, ren->branch);\n \n \t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n \t\t\t       \"writing to %s instead.\"),\n@@ -1383,8 +1383,8 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t\t\treturn -1;\n \t\tfree(alt_path);\n \t\treturn update_stages(opt, dest->path, NULL,\n-\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n+\t\t\t\t     ren->branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     ren->branch == opt->branch1 ? NULL : dest);\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n@@ -1476,12 +1476,14 @@ static int handle_change_delete(struct merge_options *opt,\n }\n \n static int handle_rename_delete(struct merge_options *opt,\n-\t\t\t\tstruct diff_filepair *pair,\n-\t\t\t\tconst char *rename_branch,\n-\t\t\t\tconst char *delete_branch)\n+\t\t\t\tstruct rename_conflict_info *ci)\n {\n-\tconst struct diff_filespec *orig = pair->one;\n-\tconst struct diff_filespec *dest = pair->two;\n+\tconst struct rename *ren = ci->ren1;\n+\tconst struct diff_filespec *orig = ren->pair->one;\n+\tconst struct diff_filespec *dest = ren->pair->two;\n+\tconst char *rename_branch = ren->branch;\n+\tconst char *delete_branch = (opt->branch1 == ren->branch ?\n+\t\t\t\t     opt->branch2 : opt->branch1);\n \n \tif (handle_change_delete(opt,\n \t\t\t\t opt->call_depth ? orig->path : dest->path,\n@@ -3184,8 +3186,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair,\n-\t\t\t\t\t\t  ci->ren1->branch))\n+\t\t\tif (handle_rename_via_dir(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3199,9 +3200,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n-\t\t\t\t\t\t ci->ren1->branch,\n-\t\t\t\t\t\t ci->ren1->branch == opt->branch1 ? opt->branch2 : opt->branch1))\n+\t\t\tif (handle_rename_delete(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372744","messageId":"20190330003336.21940-2-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 01/15] Use 'unsigned short' for mode, like diff_filespec does","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:22Z","receivedAt":"2019-03-30T00:34:18Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"struct diff_filespec defines mode to be an 'unsigned short'.  Several\nother places in the API which we'd like to interact with using a\ndiff_filespec used a plain unsigned (or unsigned int).  This caused\nproblems when taking addresses, so switch to unsigned short.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n archive.c              | 2 +-\n blame.c                | 2 +-\n blame.h                | 2 +-\n builtin/rm.c           | 2 +-\n builtin/update-index.c | 2 +-\n cache.h                | 2 +-\n fsck.c                 | 2 +-\n line-log.c             | 2 +-\n match-trees.c          | 8 ++++----\n merge-recursive.c      | 6 +++---\n notes.c                | 2 +-\n sha1-name.c            | 2 +-\n tree-diff.c            | 2 +-\n tree-walk.c            | 6 +++---\n tree-walk.h            | 6 +++---\n 15 files changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 1f98324a93..92bc001545 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -415,7 +415,7 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned int mode;\n+\t\tunsigned short mode;\n \t\tint err;\n \n \t\terr = get_tree_entry(&tree->object.oid, prefix, &tree_oid,\ndiff --git a/blame.c b/blame.c\nindex 5c07dec190..326231476e 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -99,7 +99,7 @@ static void verify_working_tree_path(struct repository *r,\n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\tif (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&\n \t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\ndiff --git a/blame.h b/blame.h\nindex be3a895043..2a285eb027 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -52,7 +52,7 @@ struct blame_origin {\n \tstruct blame_entry *suspects;\n \tmmfile_t file;\n \tstruct object_id blob_oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \t/* guilty gets set when shipping any suspects to the final\n \t * blame list instead of other commits\n \t */\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex db85b33982..90cbe896c9 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -110,7 +110,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\tconst struct cache_entry *ce;\n \t\tconst char *name = list.entry[i].name;\n \t\tstruct object_id oid;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tint local_changes = 0;\n \t\tint staged_changes = 0;\n \ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 1b6c42f748..0baf51c316 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -597,7 +597,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \ndiff --git a/cache.h b/cache.h\nindex ac92421f3a..851c3e6945 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1331,7 +1331,7 @@ static inline int hex2chr(const char *s)\n #define FALLBACK_DEFAULT_ABBREV 7\n \n struct object_context {\n-\tunsigned mode;\n+\tunsigned short mode;\n \t/*\n \t * symlink_path is only used by get_tree_entry_follow_symlinks,\n \t * and only for symlinks that point outside the repository.\ndiff --git a/fsck.c b/fsck.c\nindex 2260adb71e..4703f55561 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -604,7 +604,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \to_name = NULL;\n \n \twhile (desc.size) {\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tconst char *name;\n \t\tconst struct object_id *oid;\n \ndiff --git a/line-log.c b/line-log.c\nindex 24e21731c4..f743592bc0 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -498,7 +498,7 @@ static struct commit *check_single_commit(struct rev_info *revs)\n \n static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct object_id oid;\n \n \tif (get_tree_entry(&commit->object.oid, spec->path, &oid, &mode))\ndiff --git a/match-trees.c b/match-trees.c\nindex ddc4d39845..9d1ec8d6b0 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -140,7 +140,7 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tint score;\n \n \t\telem = tree_entry_extract(&one, &path, &mode);\n@@ -196,7 +196,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \trewrite_here = NULL;\n \twhile (desc.size) {\n \t\tconst char *name;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n \t\tif (strlen(name) == toplen &&\n@@ -285,7 +285,7 @@ void shift_tree(const struct object_id *hash1,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\tif (!*del_prefix)\n \t\t\treturn;\n@@ -313,7 +313,7 @@ void shift_tree_by(const struct object_id *hash1,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned mode1, mode2;\n+\tunsigned short mode1, mode2;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6c40c61c47..fcf37be2a7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -214,7 +214,7 @@ struct rename_conflict_info {\n  */\n struct stage_data {\n \tstruct {\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tstruct object_id oid;\n \t} stages[4];\n \tstruct rename_conflict_info *rename_conflict_info;\n@@ -482,7 +482,7 @@ static void get_files_dirs(struct merge_options *o, struct tree *tree)\n static int get_tree_entry_if_blob(const struct object_id *tree,\n \t\t\t\t  const char *path,\n \t\t\t\t  struct object_id *hashy,\n-\t\t\t\t  unsigned int *mode_o)\n+\t\t\t\t  unsigned short *mode_o)\n {\n \tint ret;\n \n@@ -1935,7 +1935,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n static int tree_has_path(struct tree *tree, const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned int mode_o;\n+\tunsigned short mode_o;\n \n \treturn !get_tree_entry(&tree->object.oid, path,\n \t\t\t       &hashy, &mode_o);\ndiff --git a/notes.c b/notes.c\nindex 7f7cc4d511..ba4cae7851 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -986,7 +986,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..d4b3d01f3c 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -1577,7 +1577,7 @@ static void diagnose_invalid_oid_path(const char *prefix,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\ndiff --git a/tree-diff.c b/tree-diff.c\nindex e6d306f69f..f1f641eb6a 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -181,7 +181,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tstruct tree_desc *t, struct tree_desc *tp,\n \tint imin)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tconst char *path;\n \tconst struct object_id *oid;\n \tint pathlen;\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 1e4bbc8a0e..ec32a47b2e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -500,7 +500,7 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n-static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned *mode)\n+static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -535,7 +535,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_\n \treturn -1;\n }\n \n-int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned *mode)\n+int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\n@@ -585,7 +585,7 @@ int get_tree_entry(const struct object_id *tree_oid, const char *name, struct ob\n  * See the code for enum get_oid_result for a description of\n  * the return values.\n  */\n-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)\n+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode)\n {\n \tint retval = MISSING_OBJECT;\n \tstruct dir_state *parents = NULL;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 8225171866..3aa381b6a3 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -16,7 +16,7 @@ struct tree_desc {\n \tunsigned int size;\n };\n \n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n@@ -51,7 +51,7 @@ struct traverse_info;\n typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);\n int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);\n \n-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);\n+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);\n \n struct traverse_info {\n \tconst char *traverse_path;\n@@ -66,7 +66,7 @@ struct traverse_info {\n \tint show_all_errors;\n };\n \n-int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned *);\n+int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);\n extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);\n extern void setup_traverse_info(struct traverse_info *info, const char *base);\n \n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372745","messageId":"20190330003336.21940-5-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 04/15] merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:25Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Since we want to replace oid,mode pairs with a single diff_filespec,\nwe will soon want to be able to use the names 'o', 'a', and 'b' for\nthe three different file versions.  Rename some local variables in\nblob_unchanged() that would otherwise conflict.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 18 ++++++++++--------\n 1 file changed, 10 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 36af5d9cc6..4ed1b48630 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -3031,9 +3031,10 @@ static int blob_unchanged(struct merge_options *opt,\n \t\t\t  unsigned a_mode,\n \t\t\t  int renormalize, const char *path)\n {\n-\tstruct strbuf o = STRBUF_INIT;\n-\tstruct strbuf a = STRBUF_INIT;\n+\tstruct strbuf obuf = STRBUF_INIT;\n+\tstruct strbuf abuf = STRBUF_INIT;\n \tint ret = 0; /* assume changed for safety */\n+\tconst struct index_state *idx = opt->repo->index;\n \n \tif (a_mode != o_mode)\n \t\treturn 0;\n@@ -3043,20 +3044,21 @@ static int blob_unchanged(struct merge_options *opt,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &obuf) ||\n+\t    read_oid_strbuf(opt, a_oid, &abuf))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n \t * performed.  Comparison can be skipped if both files are\n \t * unchanged since their sha1s have already been compared.\n \t */\n-\tif (renormalize_buffer(opt->repo->index, path, o.buf, o.len, &o) |\n-\t    renormalize_buffer(opt->repo->index, path, a.buf, a.len, &a))\n-\t\tret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));\n+\tif (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) |\n+\t    renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf))\n+\t\tret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len));\n \n error_return:\n-\tstrbuf_release(&o);\n-\tstrbuf_release(&a);\n+\tstrbuf_release(&obuf);\n+\tstrbuf_release(&abuf);\n \treturn ret;\n }\n \n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372746","messageId":"20190330003336.21940-15-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 14/15] merge-recursive: give callers of handle_content_merge() access to contents","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:35Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Pass a merge_file_info struct to handle_content_merge() so that the\ncallers can access the oid and mode of the result afterward.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 37 ++++++++++++++++++++-----------------\n 1 file changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 938a526b20..2edfa01e43 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2982,7 +2982,8 @@ static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n-static int handle_content_merge(struct merge_options *opt,\n+static int handle_content_merge(struct merge_file_info *mfi,\n+\t\t\t\tstruct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n \t\t\t\tconst struct diff_filespec *o,\n@@ -2991,7 +2992,6 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n-\tstruct merge_file_info mfi;\n \tunsigned df_conflict_remains = 0;\n \n \tif (!is_valid(o))\n@@ -3004,7 +3004,7 @@ static int handle_content_merge(struct merge_options *opt,\n \n \tif (merge_mode_and_contents(opt, o, a, b, path,\n \t\t\t\t    opt->branch1, opt->branch2,\n-\t\t\t\t    opt->call_depth * 2, &mfi))\n+\t\t\t\t    opt->call_depth * 2, mfi))\n \t\treturn -1;\n \n \t/*\n@@ -3013,13 +3013,13 @@ static int handle_content_merge(struct merge_options *opt,\n \t *   b) The merge matches what was in HEAD (content, mode, pathname)\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n-\tif (mfi.clean && was_tracked_and_matches(opt, path, &mfi.blob) &&\n+\tif (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n \t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(opt, &mfi.blob, path,\n+\t\tif (add_cacheinfo(opt, &mfi->blob, path,\n \t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n@@ -3035,11 +3035,11 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\tce = opt->repo->index->cache[pos];\n \t\t\tce->ce_flags |= CE_SKIP_WORKTREE;\n \t\t}\n-\t\treturn mfi.clean;\n+\t\treturn mfi->clean;\n \t}\n \n-\tif (!mfi.clean) {\n-\t\tif (S_ISGITLINK(mfi.blob.mode))\n+\tif (!mfi->clean) {\n+\t\tif (S_ISGITLINK(mfi->blob.mode))\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n@@ -3053,15 +3053,15 @@ static int handle_content_merge(struct merge_options *opt,\n \t\tif (opt->call_depth) {\n \t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n-\t\t\tif (!mfi.clean) {\n+\t\t\tif (!mfi->clean) {\n \t\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n \n \t\t\t\tif (update_stages(opt, path, NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? &mfi.blob : NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi.blob))\n+\t\t\t\t\t\t  file_from_stage2 ? &mfi->blob : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi->blob))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n@@ -3072,15 +3072,15 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t       path);\n \t\t}\n \t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(opt, 0, &mfi.blob, new_path)) {\n+\t\tif (update_file(opt, 0, &mfi->blob, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n-\t\tmfi.clean = 0;\n-\t} else if (update_file(opt, mfi.clean, &mfi.blob, path))\n+\t\tmfi->clean = 0;\n+\t} else if (update_file(opt, mfi->clean, &mfi->blob, path))\n \t\treturn -1;\n-\treturn !is_dirty && mfi.clean;\n+\treturn !is_dirty && mfi->clean;\n }\n \n static int handle_rename_normal(struct merge_options *opt,\n@@ -3091,7 +3091,8 @@ static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n-\treturn handle_content_merge(opt, path, was_dirty(opt, path),\n+\tstruct merge_file_info mfi;\n+\treturn handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n \t\t\t\t    o, a, b, ci);\n }\n \n@@ -3256,8 +3257,10 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t    a, b);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n+\t\t\tstruct merge_file_info mfi;\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(opt, path, is_dirty,\n+\t\t\tclean_merge = handle_content_merge(&mfi, opt, path,\n+\t\t\t\t\t\t\t   is_dirty,\n \t\t\t\t\t\t\t   o, a, b, NULL);\n \t\t}\n \t} else if (!o_valid && !a_valid && !b_valid) {\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372747","messageId":"20190330003336.21940-12-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 11/15] merge-recursive: switch from (oid,mode) pairs to a diff_filespec","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:32Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"There was a significant inconsistency in the various parts of the API\nused in merge-recursive; many places used a pair of (oid, mode) to track\nfile version/contents, while other parts used a diff_filespec (which\nhave an oid and mode embedded in it).  This inconsistency caused lots of\nplaces to need to pack and unpack data to call into other functions.\nThis has been the subject of some past cleanups (see e.g. commit\n0270a07ad0b2 (\"merge-recursive: remove final remaining caller of\nmerge_file_one()\", 2018-09-19)), but let's just remove the underlying\nmess altogether by switching to use diff_filespec.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 483 +++++++++++++++++++++-------------------------\n 1 file changed, 215 insertions(+), 268 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ada1c19ed2..1d2c9e1772 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -201,10 +201,7 @@ enum rename_type {\n  * for these (temporary) data.\n  */\n struct stage_data {\n-\tstruct {\n-\t\tunsigned short mode;\n-\t\tstruct object_id oid;\n-\t} stages[4];\n+\tstruct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */\n \tstruct rename_conflict_info *rename_conflict_info;\n \tunsigned processed:1;\n };\n@@ -269,7 +266,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \n \tci->ren1->dst_entry->processed = 0;\n \tci->ren1->dst_entry->rename_conflict_info = ci;\n-\n \tif (ren2) {\n \t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n@@ -326,14 +322,14 @@ static void output_commit_title(struct merge_options *opt, struct commit *commit\n }\n \n static int add_cacheinfo(struct merge_options *opt,\n-\t\t\t unsigned int mode, const struct object_id *oid,\n+\t\t\t const struct diff_filespec *blob,\n \t\t\t const char *path, int stage, int refresh, int options)\n {\n \tstruct index_state *istate = opt->repo->index;\n \tstruct cache_entry *ce;\n \tint ret;\n \n-\tce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);\n+\tce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0);\n \tif (!ce)\n \t\treturn err(opt, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n \n@@ -464,15 +460,14 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n \n static int get_tree_entry_if_blob(const struct object_id *tree,\n \t\t\t\t  const char *path,\n-\t\t\t\t  struct object_id *hashy,\n-\t\t\t\t  unsigned short *mode_o)\n+\t\t\t\t  struct diff_filespec *dfs)\n {\n \tint ret;\n \n-\tret = get_tree_entry(tree, path, hashy, mode_o);\n-\tif (S_ISDIR(*mode_o)) {\n-\t\toidcpy(hashy, &null_oid);\n-\t\t*mode_o = 0;\n+\tret = get_tree_entry(tree, path, &dfs->oid, &dfs->mode);\n+\tif (S_ISDIR(dfs->mode)) {\n+\t\toidcpy(&dfs->oid, &null_oid);\n+\t\tdfs->mode = 0;\n \t}\n \treturn ret;\n }\n@@ -487,12 +482,9 @@ static struct stage_data *insert_stage_data(const char *path,\n {\n \tstruct string_list_item *item;\n \tstruct stage_data *e = xcalloc(1, sizeof(struct stage_data));\n-\tget_tree_entry_if_blob(&o->object.oid, path,\n-\t\t\t       &e->stages[1].oid, &e->stages[1].mode);\n-\tget_tree_entry_if_blob(&a->object.oid, path,\n-\t\t\t       &e->stages[2].oid, &e->stages[2].mode);\n-\tget_tree_entry_if_blob(&b->object.oid, path,\n-\t\t\t       &e->stages[3].oid, &e->stages[3].mode);\n+\tget_tree_entry_if_blob(&o->object.oid, path, &e->stages[1]);\n+\tget_tree_entry_if_blob(&a->object.oid, path, &e->stages[2]);\n+\tget_tree_entry_if_blob(&b->object.oid, path, &e->stages[3]);\n \titem = string_list_insert(entries, path);\n \titem->util = e;\n \treturn e;\n@@ -648,13 +640,13 @@ static int update_stages(struct merge_options *opt, const char *path,\n \t\tif (remove_file_from_index(opt->repo->index, path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -767,7 +759,7 @@ static int dir_in_way(struct index_state *istate, const char *path,\n  * and its oid and mode match the specified values\n  */\n static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n-\t\t\t\t   const struct object_id *oid, unsigned mode)\n+\t\t\t\t   const struct diff_filespec *blob)\n {\n \tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \tstruct cache_entry *ce;\n@@ -778,7 +770,7 @@ static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n \n \t/* See if the file we were tracking before matches */\n \tce = opt->orig_index.cache[pos];\n-\treturn (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);\n+\treturn (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);\n }\n \n /*\n@@ -903,8 +895,7 @@ static int make_room_for_path(struct merge_options *opt, const char *path)\n }\n \n static int update_file_flags(struct merge_options *opt,\n-\t\t\t     const struct object_id *oid,\n-\t\t\t     unsigned mode,\n+\t\t\t     const struct diff_filespec *contents,\n \t\t\t     const char *path,\n \t\t\t     int update_cache,\n \t\t\t     int update_wd)\n@@ -919,7 +910,7 @@ static int update_file_flags(struct merge_options *opt,\n \t\tvoid *buf;\n \t\tunsigned long size;\n \n-\t\tif (S_ISGITLINK(mode)) {\n+\t\tif (S_ISGITLINK(contents->mode)) {\n \t\t\t/*\n \t\t\t * We may later decide to recursively descend into\n \t\t\t * the submodule directory and update its index\n@@ -929,14 +920,16 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\tgoto update_index;\n \t\t}\n \n-\t\tbuf = read_object_file(oid, &type, &size);\n+\t\tbuf = read_object_file(&contents->oid, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"),\n+\t\t\t\t   oid_to_hex(&contents->oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"),\n+\t\t\t\t  oid_to_hex(&contents->oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n-\t\tif (S_ISREG(mode)) {\n+\t\tif (S_ISREG(contents->mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {\n \t\t\t\tfree(buf);\n@@ -949,12 +942,11 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\tupdate_wd = 0;\n \t\t\tgoto free_buf;\n \t\t}\n-\t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n+\t\tif (S_ISREG(contents->mode) ||\n+\t\t    (!has_symlinks && S_ISLNK(contents->mode))) {\n \t\t\tint fd;\n-\t\t\tif (mode & 0100)\n-\t\t\t\tmode = 0777;\n-\t\t\telse\n-\t\t\t\tmode = 0666;\n+\t\t\tint mode = (contents->mode & 0100 ? 0777 : 0666);\n+\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n \t\t\t\tret = err(opt, _(\"failed to open '%s': %s\"),\n@@ -963,7 +955,7 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n-\t\t} else if (S_ISLNK(mode)) {\n+\t\t} else if (S_ISLNK(contents->mode)) {\n \t\t\tchar *lnk = xmemdupz(buf, size);\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n@@ -974,13 +966,13 @@ static int update_file_flags(struct merge_options *opt,\n \t\t} else\n \t\t\tret = err(opt,\n \t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t\t  mode, oid_to_hex(oid), path);\n+\t\t\t\t  contents->mode, oid_to_hex(&contents->oid), path);\n \tfree_buf:\n \t\tfree(buf);\n \t}\n update_index:\n \tif (!ret && update_cache)\n-\t\tif (add_cacheinfo(opt, mode, oid, path, 0, update_wd,\n+\t\tif (add_cacheinfo(opt, contents, path, 0, update_wd,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD))\n \t\t\treturn -1;\n \treturn ret;\n@@ -988,18 +980,17 @@ static int update_file_flags(struct merge_options *opt,\n \n static int update_file(struct merge_options *opt,\n \t\t       int clean,\n-\t\t       const struct object_id *oid,\n-\t\t       unsigned mode,\n+\t\t       const struct diff_filespec *contents,\n \t\t       const char *path)\n {\n-\treturn update_file_flags(opt, oid, mode, path, opt->call_depth || clean, !opt->call_depth);\n+\treturn update_file_flags(opt, contents, path,\n+\t\t\t\t opt->call_depth || clean, !opt->call_depth);\n }\n \n /* Low level file merging, update and removal */\n \n struct merge_file_info {\n-\tstruct object_id oid;\n-\tunsigned mode;\n+\tstruct diff_filespec blob; /* mostly use oid & mode; sometimes path */\n \tunsigned clean:1,\n \t\t merge:1;\n };\n@@ -1039,6 +1030,7 @@ static int merge_3way(struct merge_options *opt,\n \t\t}\n \t}\n \n+\tassert(a->path && b->path);\n \tif (strcmp(a->path, b->path) ||\n \t    (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {\n \t\tbase_name = opt->ancestor == NULL ? NULL :\n@@ -1140,6 +1132,11 @@ static void print_commit(struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n+static int is_valid(const struct diff_filespec *dfs)\n+{\n+\treturn dfs->mode != 0 && !is_null_oid(&dfs->oid);\n+}\n+\n static int merge_submodule(struct merge_options *opt,\n \t\t\t   struct object_id *result, const char *path,\n \t\t\t   const struct object_id *base, const struct object_id *a,\n@@ -1278,11 +1275,11 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n \t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult->mode = a->mode;\n-\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\tresult->blob.mode = a->mode;\n+\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t} else {\n-\t\t\tresult->mode = b->mode;\n-\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\tresult->blob.mode = b->mode;\n+\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))\n@@ -1292,9 +1289,9 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == o->mode)\n-\t\t\tresult->mode = b->mode;\n+\t\t\tresult->blob.mode = b->mode;\n \t\telse {\n-\t\t\tresult->mode = a->mode;\n+\t\t\tresult->blob.mode = a->mode;\n \t\t\tif (b->mode != o->mode) {\n \t\t\t\tresult->clean = 0;\n \t\t\t\tresult->merge = 1;\n@@ -1302,9 +1299,9 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))\n-\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &o->oid))\n-\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n@@ -1318,7 +1315,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \n \t\t\tif (!ret &&\n \t\t\t    write_object_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t      blob_type, &result->oid))\n+\t\t\t\t\t      blob_type, &result->blob.oid))\n \t\t\t\tret = err(opt, _(\"Unable to add %s to database\"),\n \t\t\t\t\t  a->path);\n \n@@ -1327,7 +1324,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult->clean = merge_submodule(opt, &result->oid,\n+\t\t\tresult->clean = merge_submodule(opt, &result->blob.oid,\n \t\t\t\t\t\t\to->path,\n \t\t\t\t\t\t\t&o->oid,\n \t\t\t\t\t\t\t&a->oid,\n@@ -1335,15 +1332,15 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t} else if (S_ISLNK(a->mode)) {\n \t\t\tswitch (opt->recursive_variant) {\n \t\t\tcase MERGE_RECURSIVE_NORMAL:\n-\t\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\t\tresult->clean = 0;\n \t\t\t\tbreak;\n \t\t\tcase MERGE_RECURSIVE_OURS:\n-\t\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t\t\tbreak;\n \t\t\tcase MERGE_RECURSIVE_THEIRS:\n-\t\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\t\t\tbreak;\n \t\t\t}\n \t\t} else\n@@ -1379,7 +1376,7 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t\t * index.  Instead, write to dest->path for the index but\n \t\t * only at the higher appropriate stage.\n \t\t */\n-\t\tif (update_file(opt, 0, &dest->oid, dest->mode, alt_path))\n+\t\tif (update_file(opt, 0, dest, alt_path))\n \t\t\treturn -1;\n \t\tfree(alt_path);\n \t\treturn update_stages(opt, dest->path, NULL,\n@@ -1388,16 +1385,15 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n-\tif (update_file(opt, 1, &dest->oid, dest->mode, dest->path))\n+\tif (update_file(opt, 1, dest, dest->path))\n \t\treturn -1;\n \treturn 0;\n }\n \n static int handle_change_delete(struct merge_options *opt,\n \t\t\t\tconst char *path, const char *old_path,\n-\t\t\t\tconst struct object_id *o_oid, int o_mode,\n-\t\t\t\tconst struct object_id *changed_oid,\n-\t\t\t\tint changed_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *changed,\n \t\t\t\tconst char *change_branch,\n \t\t\t\tconst char *delete_branch,\n \t\t\t\tconst char *change, const char *change_past)\n@@ -1419,7 +1415,7 @@ static int handle_change_delete(struct merge_options *opt,\n \t\t */\n \t\tret = remove_file_from_index(opt->repo->index, path);\n \t\tif (!ret)\n-\t\t\tret = update_file(opt, 0, o_oid, o_mode, update_path);\n+\t\t\tret = update_file(opt, 0, o, update_path);\n \t} else {\n \t\t/*\n \t\t * Despite the four nearly duplicate messages and argument\n@@ -1468,7 +1464,7 @@ static int handle_change_delete(struct merge_options *opt,\n \t\t * and update_wd=0, but that's a no-op.\n \t\t */\n \t\tif (change_branch != opt->branch1 || alt_path)\n-\t\t\tret = update_file(opt, 0, changed_oid, changed_mode, update_path);\n+\t\t\tret = update_file(opt, 0, changed, update_path);\n \t}\n \tfree(alt_path);\n \n@@ -1488,8 +1484,7 @@ static int handle_rename_delete(struct merge_options *opt,\n \tif (handle_change_delete(opt,\n \t\t\t\t opt->call_depth ? orig->path : dest->path,\n \t\t\t\t opt->call_depth ? NULL : orig->path,\n-\t\t\t\t &orig->oid, orig->mode,\n-\t\t\t\t &dest->oid, dest->mode,\n+\t\t\t\t orig, dest,\n \t\t\t\t rename_branch, delete_branch,\n \t\t\t\t _(\"rename\"), _(\"renamed\")))\n \t\treturn -1;\n@@ -1502,31 +1497,16 @@ static int handle_rename_delete(struct merge_options *opt,\n \t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n }\n \n-static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n-\t\t\t\t\t\t struct stage_data *entry,\n-\t\t\t\t\t\t int stage)\n-{\n-\tstruct object_id *oid = &entry->stages[stage].oid;\n-\tunsigned mode = entry->stages[stage].mode;\n-\tif (mode == 0 || is_null_oid(oid))\n-\t\treturn NULL;\n-\toidcpy(&target->oid, oid);\n-\ttarget->mode = mode;\n-\treturn target;\n-}\n-\n static int handle_file_collision(struct merge_options *opt,\n \t\t\t\t const char *collide_path,\n \t\t\t\t const char *prev_path1,\n \t\t\t\t const char *prev_path2,\n \t\t\t\t const char *branch1, const char *branch2,\n-\t\t\t\t const struct object_id *a_oid,\n-\t\t\t\t unsigned int a_mode,\n-\t\t\t\t const struct object_id *b_oid,\n-\t\t\t\t unsigned int b_mode)\n+\t\t\t\t struct diff_filespec *a,\n+\t\t\t\t struct diff_filespec *b)\n {\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec null, a, b;\n+\tstruct diff_filespec null;\n \tchar *alt_path = NULL;\n \tconst char *update_path = collide_path;\n \n@@ -1540,29 +1520,28 @@ static int handle_file_collision(struct merge_options *opt,\n \t\treturn handle_file_collision(opt, collide_path,\n \t\t\t\t\t     prev_path2, prev_path1,\n \t\t\t\t\t     branch2, branch1,\n-\t\t\t\t\t     b_oid, b_mode,\n-\t\t\t\t\t     a_oid, a_mode);\n+\t\t\t\t\t     b, a);\n \t}\n \n \t/*\n \t * In the recursive case, we just opt to undo renames\n \t */\n \tif (opt->call_depth && (prev_path1 || prev_path2)) {\n-\t\t/* Put first file (a_oid, a_mode) in its original spot */\n+\t\t/* Put first file (a->oid, a->mode) in its original spot */\n \t\tif (prev_path1) {\n-\t\t\tif (update_file(opt, 1, a_oid, a_mode, prev_path1))\n+\t\t\tif (update_file(opt, 1, a, prev_path1))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(opt, 1, a_oid, a_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, a, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\t/* Put second file (b_oid, b_mode) in its original spot */\n+\t\t/* Put second file (b->oid, b->mode) in its original spot */\n \t\tif (prev_path2) {\n-\t\t\tif (update_file(opt, 1, b_oid, b_mode, prev_path2))\n+\t\t\tif (update_file(opt, 1, b, prev_path2))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(opt, 1, b_oid, b_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, b, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n@@ -1616,26 +1595,18 @@ static int handle_file_collision(struct merge_options *opt,\n \t}\n \n \t/* Store things in diff_filespecs for functions that need it */\n-\tmemset(&a, 0, sizeof(struct diff_filespec));\n-\tmemset(&b, 0, sizeof(struct diff_filespec));\n-\tnull.path = a.path = b.path = (char *)collide_path;\n+\tnull.path = (char *)collide_path;\n \toidcpy(&null.oid, &null_oid);\n \tnull.mode = 0;\n-\toidcpy(&a.oid, a_oid);\n-\ta.mode = a_mode;\n-\ta.oid_valid = 1;\n-\toidcpy(&b.oid, b_oid);\n-\tb.mode = b_mode;\n-\tb.oid_valid = 1;\n-\n-\tif (merge_mode_and_contents(opt, &null, &a, &b, collide_path,\n+\n+\tif (merge_mode_and_contents(opt, &null, a, b, collide_path,\n \t\t\t\t    branch1, branch2, opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tmfi.clean &= !alt_path;\n-\tif (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, update_path))\n+\tif (update_file(opt, mfi.clean, &mfi.blob, update_path))\n \t\treturn -1;\n \tif (!mfi.clean && !opt->call_depth &&\n-\t    update_stages(opt, collide_path, NULL, &a, &b))\n+\t    update_stages(opt, collide_path, NULL, a, b))\n \t\treturn -1;\n \tfree(alt_path);\n \t/*\n@@ -1654,7 +1625,6 @@ static int handle_rename_add(struct merge_options *opt,\n \t/* a was renamed to c, and a separate c was added. */\n \tstruct diff_filespec *a = ci->ren1->pair->one;\n \tstruct diff_filespec *c = ci->ren1->pair->two;\n-\tstruct diff_filespec tmp;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1669,23 +1639,21 @@ static int handle_rename_add(struct merge_options *opt,\n \t       a->path, c->path, rename_branch,\n \t       c->path, add_branch);\n \n-\tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n-\ttmp.path = a->path;\n-\n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(opt, a, c, &tmp,\n+\tif (merge_mode_and_contents(opt, a, c,\n+\t\t\t\t    &ci->ren1->src_entry->stages[other_stage],\n \t\t\t\t    prev_path_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(prev_path_desc);\n \n+\tci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path;\n \treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     rename_branch, add_branch,\n-\t\t\t\t     &mfi.oid, mfi.mode,\n-\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n-\t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n+\t\t\t\t     &mfi.blob,\n+\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage]);\n }\n \n static char *find_path_for_conflict(struct merge_options *opt,\n@@ -1714,7 +1682,6 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n {\n \t/* One file was renamed in both branches, but to different names. */\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n \tstruct diff_filespec *o = ci->ren1->pair->one;\n \tstruct diff_filespec *a = ci->ren1->pair->two;\n@@ -1743,7 +1710,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, o->path))\n+\t\tif (update_file(opt, 0, &mfi.blob, o->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -1754,16 +1721,16 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * such cases, we should keep the added file around,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n-\t\tif (add) {\n-\t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n+\t\tadd = &ci->ren1->dst_entry->stages[2 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tif (update_file(opt, 0, add, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n \t\t\tremove_file_from_index(opt->repo->index, a->path);\n-\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n-\t\tif (add) {\n-\t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n+\t\tadd = &ci->ren2->dst_entry->stages[3 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tif (update_file(opt, 0, add, b->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n@@ -1774,40 +1741,42 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * rename/add collision.  If not, we can write the file out\n \t\t * to the specified location.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n-\t\tif (add) {\n+\t\tadd = &ci->ren1->dst_entry->stages[2 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tadd->path = mfi.blob.path = a->path;\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->ren1->branch,\n \t\t\t\t\t\t  ci->ren2->branch,\n-\t\t\t\t\t\t  &mfi.oid, mfi.mode,\n-\t\t\t\t\t\t  &add->oid, add->mode) < 0)\n+\t\t\t\t\t\t  &mfi.blob, add) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n \t\t\t\t\t\t\t\tci->ren1->branch,\n \t\t\t\t\t\t\t\tci->ren2->branch);\n-\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n+\t\t\tif (update_file(opt, 0, &mfi.blob,\n+\t\t\t\t\tnew_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n \t\t\tif (update_stages(opt, a->path, NULL, a, NULL))\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n-\t\tif (add) {\n+\t\tadd = &ci->ren2->dst_entry->stages[3 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tadd->path = mfi.blob.path = b->path;\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->ren1->branch,\n \t\t\t\t\t\t  ci->ren2->branch,\n-\t\t\t\t\t\t  &add->oid, add->mode,\n-\t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n+\t\t\t\t\t\t  add, &mfi.blob) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n \t\t\t\t\t\t\t\tci->ren2->branch,\n \t\t\t\t\t\t\t\tci->ren1->branch);\n-\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n+\t\t\tif (update_file(opt, 0, &mfi.blob,\n+\t\t\t\t\tnew_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n \t\t\tif (update_stages(opt, b->path, NULL, NULL, b))\n@@ -1826,12 +1795,12 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tstruct diff_filespec *b = ci->ren2->pair->one;\n \tstruct diff_filespec *c1 = ci->ren1->pair->two;\n \tstruct diff_filespec *c2 = ci->ren2->pair->two;\n-\tstruct diff_filespec tmp1, tmp2;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ostage1, ostage2;\n \n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1839,27 +1808,31 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t       a->path, c1->path, ci->ren1->branch,\n \t       b->path, c2->path, ci->ren2->branch);\n \n-\tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n-\ttmp1.path = a->path;\n-\tfilespec_from_entry(&tmp2, ci->ren2->src_entry, 2);\n-\ttmp2.path = b->path;\n-\n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(opt, a, c1, &tmp1, path_side_1_desc,\n+\tostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2;\n+\tostage2 = ostage1 ^ 1;\n+\tci->ren1->src_entry->stages[ostage1].path = a->path;\n+\tci->ren2->src_entry->stages[ostage2].path = b->path;\n+\tif (merge_mode_and_contents(opt, a, c1,\n+\t\t\t\t    &ci->ren1->src_entry->stages[ostage1],\n+\t\t\t\t    path_side_1_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(opt, b, &tmp2, c2, path_side_2_desc,\n+\t    merge_mode_and_contents(opt, b,\n+\t\t\t\t    &ci->ren2->src_entry->stages[ostage2],\n+\t\t\t\t    c2, path_side_2_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n \tfree(path_side_1_desc);\n \tfree(path_side_2_desc);\n+\tmfi_c1.blob.path = path;\n+\tmfi_c2.blob.path = path;\n \n \treturn handle_file_collision(opt, path, a->path, b->path,\n \t\t\t\t     ci->ren1->branch, ci->ren2->branch,\n-\t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n-\t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n+\t\t\t\t     &mfi_c1.blob, &mfi_c2.blob);\n }\n \n /*\n@@ -2722,7 +2695,6 @@ static int process_renames(struct merge_options *opt,\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n \t\t\t\t\t\t   opt, ren1, ren2);\n-\n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n \t\t\t/* we only use sha1 and mode of these */\n@@ -2771,8 +2743,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * update_file().\n \t\t\t\t */\n \t\t\t\tif (update_file_flags(opt,\n-\t\t\t\t\t\t      &ren1->pair->two->oid,\n-\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t      ren1->pair->two,\n \t\t\t\t\t\t      ren1_dst,\n \t\t\t\t\t\t      1, /* update_cache */\n \t\t\t\t\t\t      0  /* update_wd    */))\n@@ -2923,11 +2894,6 @@ static void final_cleanup_renames(struct rename_info *re_info)\n \tfinal_cleanup_rename(re_info->merge_renames);\n }\n \n-static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n-{\n-\treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n-}\n-\n static int read_oid_strbuf(struct merge_options *opt,\n \t\t\t   const struct object_id *oid,\n \t\t\t   struct strbuf *dst)\n@@ -2947,10 +2913,8 @@ static int read_oid_strbuf(struct merge_options *opt,\n }\n \n static int blob_unchanged(struct merge_options *opt,\n-\t\t\t  const struct object_id *o_oid,\n-\t\t\t  unsigned o_mode,\n-\t\t\t  const struct object_id *a_oid,\n-\t\t\t  unsigned a_mode,\n+\t\t\t  const struct diff_filespec *o,\n+\t\t\t  const struct diff_filespec *a,\n \t\t\t  int renormalize, const char *path)\n {\n \tstruct strbuf obuf = STRBUF_INIT;\n@@ -2958,16 +2922,15 @@ static int blob_unchanged(struct merge_options *opt,\n \tint ret = 0; /* assume changed for safety */\n \tconst struct index_state *idx = opt->repo->index;\n \n-\tif (a_mode != o_mode)\n+\tif (a->mode != o->mode)\n \t\treturn 0;\n-\tif (oid_eq(o_oid, a_oid))\n+\tif (oid_eq(&o->oid, &a->oid))\n \t\treturn 1;\n \tif (!renormalize)\n \t\treturn 0;\n \n-\tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(opt, o_oid, &obuf) ||\n-\t    read_oid_strbuf(opt, a_oid, &abuf))\n+\tif (read_oid_strbuf(opt, &o->oid, &obuf) ||\n+\t    read_oid_strbuf(opt, &a->oid, &abuf))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -2986,30 +2949,26 @@ static int blob_unchanged(struct merge_options *opt,\n \n static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\tconst char *path,\n-\t\t\t\tstruct object_id *o_oid, int o_mode,\n-\t\t\t\tstruct object_id *a_oid, int a_mode,\n-\t\t\t\tstruct object_id *b_oid, int b_mode)\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b)\n {\n \tconst char *modify_branch, *delete_branch;\n-\tstruct object_id *changed_oid;\n-\tint changed_mode;\n+\tconst struct diff_filespec *changed;\n \n-\tif (a_oid) {\n+\tif (is_valid(a)) {\n \t\tmodify_branch = opt->branch1;\n \t\tdelete_branch = opt->branch2;\n-\t\tchanged_oid = a_oid;\n-\t\tchanged_mode = a_mode;\n+\t\tchanged = a;\n \t} else {\n \t\tmodify_branch = opt->branch2;\n \t\tdelete_branch = opt->branch1;\n-\t\tchanged_oid = b_oid;\n-\t\tchanged_mode = b_mode;\n+\t\tchanged = b;\n \t}\n \n \treturn handle_change_delete(opt,\n \t\t\t\t    path, NULL,\n-\t\t\t\t    o_oid, o_mode,\n-\t\t\t\t    changed_oid, changed_mode,\n+\t\t\t\t    o, changed,\n \t\t\t\t    modify_branch, delete_branch,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n@@ -3017,50 +2976,24 @@ static int handle_modify_delete(struct merge_options *opt,\n static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n-\t\t\t\tstruct object_id *o_oid, int o_mode,\n-\t\t\t\tstruct object_id *a_oid, int a_mode,\n-\t\t\t\tstruct object_id *b_oid, int b_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n-\tconst char *path1 = NULL, *path2 = NULL;\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec one, a, b;\n \tunsigned df_conflict_remains = 0;\n \n-\tif (!o_oid) {\n+\tif (!is_valid(o))\n \t\treason = _(\"add/add\");\n-\t\to_oid = (struct object_id *)&null_oid;\n-\t}\n-\tone.path = a.path = b.path = (char *)path;\n-\toidcpy(&one.oid, o_oid);\n-\tone.mode = o_mode;\n-\toidcpy(&a.oid, a_oid);\n-\ta.mode = a_mode;\n-\toidcpy(&b.oid, b_oid);\n-\tb.mode = b_mode;\n-\n-\tif (ci) {\n-\t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n-\n-\t\tpath1 = (opt->branch1 == ci->ren1->branch) ?\n-\t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If ci->ren2->pair != NULL, we are in\n-\t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n-\t\t * normal rename.\n-\t\t */\n-\t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n-\t\t\t opt->branch2 == ci->ren1->branch) ?\n-\t\t\tpair1->two->path : pair1->one->path;\n-\t\tone.path = pair1->one->path;\n-\t\ta.path = (char *)path1;\n-\t\tb.path = (char *)path2;\n-\n-\t\tif (dir_in_way(opt->repo->index, path, !opt->call_depth,\n-\t\t\t       S_ISGITLINK(pair1->two->mode)))\n-\t\t\tdf_conflict_remains = 1;\n-\t}\n-\tif (merge_mode_and_contents(opt, &one, &a, &b, path,\n+\n+\tassert(o->path && a->path && b->path);\n+\tif (ci && dir_in_way(opt->repo->index, path, !opt->call_depth,\n+\t\t\t     S_ISGITLINK(ci->ren1->pair->two->mode)))\n+\t\tdf_conflict_remains = 1;\n+\n+\tif (merge_mode_and_contents(opt, o, a, b, path,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -3071,14 +3004,13 @@ static int handle_content_merge(struct merge_options *opt,\n \t *   b) The merge matches what was in HEAD (content, mode, pathname)\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n-\tif (mfi.clean &&\n-\t    was_tracked_and_matches(opt, path, &mfi.oid, mfi.mode) &&\n+\tif (mfi.clean && was_tracked_and_matches(opt, path, &mfi.blob) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n \t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(opt, mfi.mode, &mfi.oid, path,\n+\t\tif (add_cacheinfo(opt, &mfi.blob, path,\n \t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n@@ -3098,12 +3030,12 @@ static int handle_content_merge(struct merge_options *opt,\n \t}\n \n \tif (!mfi.clean) {\n-\t\tif (S_ISGITLINK(mfi.mode))\n+\t\tif (S_ISGITLINK(mfi.blob.mode))\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (ci && !df_conflict_remains)\n-\t\t\tif (update_stages(opt, path, &one, &a, &b))\n+\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -3113,17 +3045,14 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(opt, path, &one, &a, &b))\n+\t\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n-\t\t\t\tstruct diff_filespec merged;\n-\t\t\t\toidcpy(&merged.oid, &mfi.oid);\n-\t\t\t\tmerged.mode = mfi.mode;\n \n \t\t\t\tif (update_stages(opt, path, NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\t\t  file_from_stage2 ? &mfi.blob : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi.blob))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n@@ -3134,28 +3063,27 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t       path);\n \t\t}\n \t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\tif (update_file(opt, 0, &mfi.blob, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else if (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t} else if (update_file(opt, mfi.clean, &mfi.blob, path))\n \t\treturn -1;\n \treturn !is_dirty && mfi.clean;\n }\n \n static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst char *path,\n-\t\t\t\tstruct object_id *o_oid, unsigned int o_mode,\n-\t\t\t\tstruct object_id *a_oid, unsigned int a_mode,\n-\t\t\t\tstruct object_id *b_oid, unsigned int b_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n \treturn handle_content_merge(opt, path, was_dirty(opt, path),\n-\t\t\t\t    o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,\n-\t\t\t\t    ci);\n+\t\t\t\t    o, a, b, ci);\n }\n \n /* Per entry merge function */\n@@ -3164,24 +3092,36 @@ static int process_entry(struct merge_options *opt,\n {\n \tint clean_merge = 1;\n \tint normalize = opt->renormalize;\n-\tunsigned o_mode = entry->stages[1].mode;\n-\tunsigned a_mode = entry->stages[2].mode;\n-\tunsigned b_mode = entry->stages[3].mode;\n-\tstruct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode);\n-\tstruct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode);\n-\tstruct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode);\n+\n+\tstruct diff_filespec *o = &entry->stages[1];\n+\tstruct diff_filespec *a = &entry->stages[2];\n+\tstruct diff_filespec *b = &entry->stages[3];\n+\tint o_valid = is_valid(o);\n+\tint a_valid = is_valid(a);\n+\tint b_valid = is_valid(b);\n+\to->path = a->path = b->path = (char*)path;\n \n \tentry->processed = 1;\n \tif (entry->rename_conflict_info) {\n \t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n+\t\tstruct diff_filespec *temp;\n+\n+\t\t/*\n+\t\t * For cases with a single rename, {o,a,b}->path have all been\n+\t\t * set to the rename target path; we need to set two of these\n+\t\t * back to the rename source.\n+\t\t * For rename/rename conflicts, we'll manually fix paths below.\n+\t\t */\n+\t\ttemp = (opt->branch1 == ci->ren1->branch) ? b : a;\n+\t\to->path = temp->path = ci->ren1->pair->one->path;\n+\t\tif (ci->ren2) {\n+\t\t\tassert(opt->branch1 == ci->ren1->branch);\n+\t\t}\n+\n \t\tswitch (ci->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n-\t\t\tclean_merge = handle_rename_normal(opt,\n-\t\t\t\t\t\t\t   path,\n-\t\t\t\t\t\t\t   o_oid, o_mode,\n-\t\t\t\t\t\t\t   a_oid, a_mode,\n-\t\t\t\t\t\t\t   b_oid, b_mode,\n+\t\t\tclean_merge = handle_rename_normal(opt, path, o, a, b,\n \t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n@@ -3204,11 +3144,27 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n+\t\t\t/*\n+\t\t\t * Manually fix up paths; note:\n+\t\t\t * ren[12]->pair->one->path are equal.\n+\t\t\t */\n+\t\t\to->path = ci->ren1->pair->one->path;\n+\t\t\ta->path = ci->ren1->pair->two->path;\n+\t\t\tb->path = ci->ren2->pair->two->path;\n+\n \t\t\tclean_merge = 0;\n \t\t\tif (handle_rename_rename_1to2(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n+\t\t\t/*\n+\t\t\t * Manually fix up paths; note,\n+\t\t\t * ren[12]->pair->two->path are actually equal.\n+\t\t\t */\n+\t\t\to->path = NULL;\n+\t\t\ta->path = ci->ren1->pair->two->path;\n+\t\t\tb->path = ci->ren2->pair->two->path;\n+\n \t\t\t/*\n \t\t\t * Probably unclean merge, but if the two renamed\n \t\t\t * files merge cleanly and the two resulting files\n@@ -3221,57 +3177,53 @@ static int process_entry(struct merge_options *opt,\n \t\t\tentry->processed = 0;\n \t\t\tbreak;\n \t\t}\n-\t} else if (o_oid && (!a_oid || !b_oid)) {\n+\t} else if (o_valid && (!a_valid || !b_valid)) {\n \t\t/* Case A: Deleted in one */\n-\t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(opt, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(opt, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\tif ((!a_valid && !b_valid) ||\n+\t\t    (!b_valid && blob_unchanged(opt, o, a, normalize, path)) ||\n+\t\t    (!a_valid && blob_unchanged(opt, o, b, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n-\t\t\tif (a_oid)\n+\t\t\tif (a_valid)\n \t\t\t\toutput(opt, 2, _(\"Removing %s\"), path);\n \t\t\t/* do not touch working file if it did not exist */\n-\t\t\tremove_file(opt, 1, path, !a_oid);\n+\t\t\tremove_file(opt, 1, path, !a_valid);\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_modify_delete(opt, path, o_oid, o_mode,\n-\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n+\t\t\tif (handle_modify_delete(opt, path, o, a, b))\n \t\t\t\tclean_merge = -1;\n \t\t}\n-\t} else if ((!o_oid && a_oid && !b_oid) ||\n-\t\t   (!o_oid && !a_oid && b_oid)) {\n+\t} else if ((!o_valid && a_valid && !b_valid) ||\n+\t\t   (!o_valid && !a_valid && b_valid)) {\n \t\t/* Case B: Added in one. */\n \t\t/* [nothing|directory] -> ([nothing|directory], file) */\n \n \t\tconst char *add_branch;\n \t\tconst char *other_branch;\n-\t\tunsigned mode;\n-\t\tconst struct object_id *oid;\n \t\tconst char *conf;\n+\t\tconst struct diff_filespec *contents;\n \n-\t\tif (a_oid) {\n+\t\tif (a_valid) {\n \t\t\tadd_branch = opt->branch1;\n \t\t\tother_branch = opt->branch2;\n-\t\t\tmode = a_mode;\n-\t\t\toid = a_oid;\n+\t\t\tcontents = a;\n \t\t\tconf = _(\"file/directory\");\n \t\t} else {\n \t\t\tadd_branch = opt->branch2;\n \t\t\tother_branch = opt->branch1;\n-\t\t\tmode = b_mode;\n-\t\t\toid = b_oid;\n+\t\t\tcontents = b;\n \t\t\tconf = _(\"directory/file\");\n \t\t}\n \t\tif (dir_in_way(opt->repo->index, path,\n-\t\t\t       !opt->call_depth && !S_ISGITLINK(a_mode),\n+\t\t\t       !opt->call_depth && !S_ISGITLINK(a->mode),\n \t\t\t       0)) {\n \t\t\tchar *new_path = unique_path(opt, path, add_branch);\n \t\t\tclean_merge = 0;\n \t\t\toutput(opt, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tif (update_file(opt, 0, oid, mode, new_path))\n+\t\t\tif (update_file(opt, 0, contents, new_path))\n \t\t\t\tclean_merge = -1;\n \t\t\telse if (opt->call_depth)\n \t\t\t\tremove_file_from_index(opt->repo->index, path);\n@@ -3279,11 +3231,11 @@ static int process_entry(struct merge_options *opt,\n \t\t} else {\n \t\t\toutput(opt, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tif (update_file_flags(opt, oid, mode, path, 1, !a_oid))\n+\t\t\tif (update_file_flags(opt, contents, path, 1, !a_valid))\n \t\t\t\tclean_merge = -1;\n \t\t}\n-\t} else if (a_oid && b_oid) {\n-\t\tif (!o_oid) {\n+\t} else if (a_valid && b_valid) {\n+\t\tif (!o_valid) {\n \t\t\t/* Case C: Added in both (check for same permissions) */\n \t\t\toutput(opt, 1,\n \t\t\t       _(\"CONFLICT (add/add): Merge conflict in %s\"),\n@@ -3292,24 +3244,19 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t    path, NULL, NULL,\n \t\t\t\t\t\t\t    opt->branch1,\n \t\t\t\t\t\t\t    opt->branch2,\n-\t\t\t\t\t\t\t    a_oid, a_mode,\n-\t\t\t\t\t\t\t    b_oid, b_mode);\n+\t\t\t\t\t\t\t    a, b);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(opt, path,\n-\t\t\t\t\t\t\t   is_dirty,\n-\t\t\t\t\t\t\t   o_oid, o_mode,\n-\t\t\t\t\t\t\t   a_oid, a_mode,\n-\t\t\t\t\t\t\t   b_oid, b_mode,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\tclean_merge = handle_content_merge(opt, path, is_dirty,\n+\t\t\t\t\t\t\t   o, a, b, NULL);\n \t\t}\n-\t} else if (!o_oid && !a_oid && !b_oid) {\n+\t} else if (!o_valid && !a_valid && !b_valid) {\n \t\t/*\n \t\t * this entry was deleted altogether. a_mode == 0 means\n \t\t * we had that path and want to actively remove it.\n \t\t */\n-\t\tremove_file(opt, 1, path, !a_mode);\n+\t\tremove_file(opt, 1, path, !a->mode);\n \t} else\n \t\tBUG(\"fatal merge failure, shouldn't happen.\");\n \n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372748","messageId":"20190330003336.21940-8-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 07/15] merge-recursive: shrink rename_conflict_info","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:28Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The rename_conflict_info struct used both a pair and a stage_data which\nwere taken from a rename struct.  Just use the original rename struct.\nThis will also allow us to start making other simplifications to the\ncode.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 120 +++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 70 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c4a2ef2a37..e05f8f22f5 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -238,24 +238,20 @@ struct rename {\n \n struct rename_conflict_info {\n \tenum rename_type rename_type;\n-\tstruct diff_filepair *pair1;\n-\tstruct diff_filepair *pair2;\n+\tstruct rename *ren1;\n+\tstruct rename *ren2;\n \tconst char *branch1;\n \tconst char *branch2;\n-\tstruct stage_data *dst_entry1;\n-\tstruct stage_data *dst_entry2;\n \tstruct diff_filespec ren1_other;\n \tstruct diff_filespec ren2_other;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n-\t\t\t\t\t      struct diff_filepair *pair1,\n-\t\t\t\t\t      struct diff_filepair *pair2,\n+\t\t\t\t\t      struct merge_options *opt,\n+\t\t\t\t\t      struct rename *ren1,\n+\t\t\t\t\t      struct rename *ren2,\n \t\t\t\t\t      const char *branch1,\n \t\t\t\t\t      const char *branch2,\n-\t\t\t\t\t      struct stage_data *dst_entry1,\n-\t\t\t\t\t      struct stage_data *dst_entry2,\n-\t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct stage_data *src_entry1,\n \t\t\t\t\t      struct stage_data *src_entry2)\n {\n@@ -269,31 +265,27 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (dst_entry2 && branch1 != opt->branch1) {\n+\tif (ren2 && branch1 != opt->branch1) {\n \t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t   pair2,      pair1,\n-\t\t\t\t\t   branch2,    branch1,\n-\t\t\t\t\t   dst_entry2, dst_entry1,\n \t\t\t\t\t   opt,\n+\t\t\t\t\t   ren2,       ren1,\n+\t\t\t\t\t   branch2,    branch1,\n \t\t\t\t\t   src_entry2, src_entry1);\n \t\treturn;\n \t}\n \n \tci = xcalloc(1, sizeof(struct rename_conflict_info));\n \tci->rename_type = rename_type;\n-\tci->pair1 = pair1;\n+\tci->ren1 = ren1;\n+\tci->ren2 = ren2;\n \tci->branch1 = branch1;\n \tci->branch2 = branch2;\n \n-\tci->dst_entry1 = dst_entry1;\n-\tdst_entry1->rename_conflict_info = ci;\n-\tdst_entry1->processed = 0;\n+\tci->ren1->dst_entry->processed = 0;\n+\tci->ren1->dst_entry->rename_conflict_info = ci;\n \n-\tassert(!pair2 == !dst_entry2);\n-\tif (dst_entry2) {\n-\t\tci->dst_entry2 = dst_entry2;\n-\t\tci->pair2 = pair2;\n-\t\tdst_entry2->rename_conflict_info = ci;\n+\tif (ren2) {\n+\t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n \n \t/*\n@@ -305,7 +297,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n \t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n \n-\t\tci->ren1_other.path = pair1->one->path;\n+\t\tci->ren1_other.path = ren1->pair->one->path;\n \t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n \t\tci->ren1_other.mode = src_entry1->stages[ostage1].mode;\n \t}\n@@ -313,7 +305,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tif (rename_type == RENAME_TWO_FILES_TO_ONE) {\n \t\tostage2 = ostage1 ^ 1;\n \n-\t\tci->ren2_other.path = pair2->one->path;\n+\t\tci->ren2_other.path = ren2->pair->one->path;\n \t\toidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);\n \t\tci->ren2_other.mode = src_entry2->stages[ostage2].mode;\n \t}\n@@ -1694,8 +1686,8 @@ static int handle_rename_add(struct merge_options *opt,\n \t\t\t     struct rename_conflict_info *ci)\n {\n \t/* a was renamed to c, and a separate c was added. */\n-\tstruct diff_filespec *a = ci->pair1->one;\n-\tstruct diff_filespec *c = ci->pair1->two;\n+\tstruct diff_filespec *a = ci->ren1->pair->one;\n+\tstruct diff_filespec *c = ci->ren1->pair->two;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1718,8 +1710,8 @@ static int handle_rename_add(struct merge_options *opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi.oid, mfi.mode,\n-\t\t\t\t     &ci->dst_entry1->stages[other_stage].oid,\n-\t\t\t\t     ci->dst_entry1->stages[other_stage].mode);\n+\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n+\t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n }\n \n static char *find_path_for_conflict(struct merge_options *opt,\n@@ -1750,9 +1742,9 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \tstruct merge_file_info mfi;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n-\tstruct diff_filespec *o = ci->pair1->one;\n-\tstruct diff_filespec *a = ci->pair1->two;\n-\tstruct diff_filespec *b = ci->pair2->two;\n+\tstruct diff_filespec *o = ci->ren1->pair->one;\n+\tstruct diff_filespec *a = ci->ren1->pair->two;\n+\tstruct diff_filespec *b = ci->ren2->pair->two;\n \tchar *path_desc;\n \n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n@@ -1788,14 +1780,14 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * such cases, we should keep the added file around,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n \t\tif (add) {\n \t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n \t\t\tremove_file_from_index(opt->repo->index, a->path);\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n \t\tif (add) {\n \t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n \t\t\t\treturn -1;\n@@ -1808,7 +1800,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * rename/add collision.  If not, we can write the file out\n \t\t * to the specified location.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n@@ -1827,7 +1819,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n@@ -1854,10 +1846,10 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n-\tstruct diff_filespec *a = ci->pair1->one;\n-\tstruct diff_filespec *b = ci->pair2->one;\n-\tstruct diff_filespec *c1 = ci->pair1->two;\n-\tstruct diff_filespec *c2 = ci->pair2->two;\n+\tstruct diff_filespec *a = ci->ren1->pair->one;\n+\tstruct diff_filespec *b = ci->ren2->pair->one;\n+\tstruct diff_filespec *c1 = ci->ren1->pair->two;\n+\tstruct diff_filespec *c2 = ci->ren2->pair->two;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n@@ -2732,13 +2724,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t     ren2->pair->two);\n \t\t\t}\n \t\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t\t   ren1->pair,\n-\t\t\t\t\t\t   ren2->pair,\n+\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t   ren1,\n+\t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t   NULL);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n@@ -2759,13 +2749,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\tren2->src_entry->processed = 1;\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n-\t\t\t\t\t\t   ren1->pair,\n-\t\t\t\t\t\t   ren2->pair,\n+\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t   ren1,\n+\t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t   ren2->src_entry);\n \n@@ -2802,24 +2790,20 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n \t\t\t    ren1->add_turned_into_rename) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n@@ -2848,13 +2832,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * file, then the merge will be clean.\n \t\t\t\t */\n \t\t\t\tsetup_rename_conflict_info(RENAME_ADD,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else\n@@ -2876,13 +2858,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t}\n \t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n@@ -3119,15 +3099,15 @@ static int handle_content_merge(struct merge_options *opt,\n \tb.mode = b_mode;\n \n \tif (ci) {\n-\t\tstruct diff_filepair *pair1 = ci->pair1;\n+\t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n \n \t\tpath1 = (opt->branch1 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If ci->pair2 != NULL, we are in\n+\t\t/* If ci->ren2->pair != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n-\t\tpath2 = (ci->pair2 ||\n+\t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n \t\t\t opt->branch2 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n@@ -3264,7 +3244,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->pair1, ci->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair, ci->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3278,7 +3258,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt, ci->pair1,\n+\t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n \t\t\t\t\t\t ci->branch1, ci->branch2))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372749","messageId":"20190330003336.21940-13-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 12/15] t6043: fix copied test description to match its purpose","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:33Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-rename-directories.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh\nindex 62c564707b..fe205be607 100755\n--- a/t/t6043-merge-rename-directories.sh\n+++ b/t/t6043-merge-rename-directories.sh\n@@ -3910,7 +3910,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '\n #         To which, I can do no more than shrug my shoulders and say that\n #         even simple rules give weird results when given weird inputs.\n \n-test_expect_success '12b-setup: Moving one directory hierarchy into another' '\n+test_expect_success '12b-setup: Moving two directory hierarchies into each other' '\n \ttest_create_repo 12b &&\n \t(\n \t\tcd 12b &&\n@@ -3940,7 +3940,7 @@ test_expect_success '12b-setup: Moving one directory hierarchy into another' '\n \t)\n '\n \n-test_expect_success '12b-check: Moving one directory hierarchy into another' '\n+test_expect_success '12b-check: Moving two directory hierarchies into each other' '\n \t(\n \t\tcd 12b &&\n \n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372750","messageId":"20190330003336.21940-10-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 09/15] merge-recursive: track branch where rename occurred in rename struct","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:30Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"We previously tracked the branch associated with a rename in a separate\nfield in rename_conflict_info, but since it is directly associated with\nthe rename it makes more sense to move it into the rename struct.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 113 +++++++++++++++++-----------------------------\n 1 file changed, 42 insertions(+), 71 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e66b47cfa1..f85c276f35 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -211,6 +211,7 @@ struct stage_data {\n \n struct rename {\n \tstruct diff_filepair *pair;\n+\tconst char *branch; /* branch that the rename occurred on */\n \t/*\n \t * Purpose of src_entry and dst_entry:\n \t *\n@@ -240,16 +241,12 @@ struct rename_conflict_info {\n \tenum rename_type rename_type;\n \tstruct rename *ren1;\n \tstruct rename *ren2;\n-\tconst char *branch1;\n-\tconst char *branch2;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct rename *ren1,\n-\t\t\t\t\t      struct rename *ren2,\n-\t\t\t\t\t      const char *branch1,\n-\t\t\t\t\t      const char *branch2)\n+\t\t\t\t\t      struct rename *ren2)\n {\n \tstruct rename_conflict_info *ci;\n \n@@ -260,11 +257,8 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (ren2 && branch1 != opt->branch1) {\n-\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t   opt,\n-\t\t\t\t\t   ren2,       ren1,\n-\t\t\t\t\t   branch2,    branch1);\n+\tif (ren2 && ren1->branch != opt->branch1) {\n+\t\tsetup_rename_conflict_info(rename_type, opt, ren2, ren1);\n \t\treturn;\n \t}\n \n@@ -272,8 +266,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tci->rename_type = rename_type;\n \tci->ren1 = ren1;\n \tci->ren2 = ren2;\n-\tci->branch1 = branch1;\n-\tci->branch2 = branch2;\n \n \tci->ren1->dst_entry->processed = 0;\n \tci->ren1->dst_entry->rename_conflict_info = ci;\n@@ -1665,12 +1657,15 @@ static int handle_rename_add(struct merge_options *opt,\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n \n-\tint other_stage = (ci->branch1 == opt->branch1 ? 3 : 2);\n+\tconst char *rename_branch = ci->ren1->branch;\n+\tconst char *add_branch = (opt->branch1 == rename_branch ?\n+\t\t\t\t  opt->branch2 : opt->branch1);\n+\tint other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2);\n \n \toutput(opt, 1, _(\"CONFLICT (rename/add): \"\n \t       \"Rename %s->%s in %s.  Added %s in %s\"),\n-\t       a->path, c->path, ci->branch1,\n-\t       c->path, ci->branch2);\n+\t       a->path, c->path, rename_branch,\n+\t       c->path, add_branch);\n \n \tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n \ttmp.path = a->path;\n@@ -1685,7 +1680,7 @@ static int handle_rename_add(struct merge_options *opt,\n \n \treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n-\t\t\t\t     ci->branch1, ci->branch2,\n+\t\t\t\t     rename_branch, add_branch,\n \t\t\t\t     &mfi.oid, mfi.mode,\n \t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n \t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n@@ -1727,14 +1722,14 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n-\t       o->path, a->path, ci->branch1,\n-\t       o->path, b->path, ci->branch2,\n+\t       o->path, a->path, ci->ren1->branch,\n+\t       o->path, b->path, ci->ren2->branch,\n \t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n \t\t\t    a->path, b->path, o->path);\n \tif (merge_mode_and_contents(opt, o, a, b, path_desc,\n-\t\t\t\t    ci->branch1, ci->branch2,\n+\t\t\t\t    ci->ren1->branch, ci->ren2->branch,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(path_desc);\n@@ -1781,14 +1776,15 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n-\t\t\t\t\t\t  ci->branch1, ci->branch2,\n+\t\t\t\t\t\t  ci->ren1->branch,\n+\t\t\t\t\t\t  ci->ren2->branch,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode,\n \t\t\t\t\t\t  &add->oid, add->mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n-\t\t\t\t\t\t\t\tci->branch1,\n-\t\t\t\t\t\t\t\tci->branch2);\n+\t\t\t\t\t\t\t\tci->ren1->branch,\n+\t\t\t\t\t\t\t\tci->ren2->branch);\n \t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n@@ -1800,14 +1796,15 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n-\t\t\t\t\t\t  ci->branch1, ci->branch2,\n+\t\t\t\t\t\t  ci->ren1->branch,\n+\t\t\t\t\t\t  ci->ren2->branch,\n \t\t\t\t\t\t  &add->oid, add->mode,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n-\t\t\t\t\t\t\t\tci->branch2,\n-\t\t\t\t\t\t\t\tci->branch1);\n+\t\t\t\t\t\t\t\tci->ren2->branch,\n+\t\t\t\t\t\t\t\tci->ren1->branch);\n \t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n@@ -1837,8 +1834,8 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n \t       \"Rename %s->%s in %s\"),\n-\t       a->path, c1->path, ci->branch1,\n-\t       b->path, c2->path, ci->branch2);\n+\t       a->path, c1->path, ci->ren1->branch,\n+\t       b->path, c2->path, ci->ren2->branch);\n \n \tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n \ttmp1.path = a->path;\n@@ -1858,7 +1855,7 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tfree(path_side_2_desc);\n \n \treturn handle_file_collision(opt, path, a->path, b->path,\n-\t\t\t\t     ci->branch1, ci->branch2,\n+\t\t\t\t     ci->ren1->branch, ci->ren2->branch,\n \t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n \t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n }\n@@ -2542,6 +2539,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n  * information; tree is always equal to either a_tree or b_tree.\n  */\n static struct string_list *get_renames(struct merge_options *opt,\n+\t\t\t\t       const char *branch,\n \t\t\t\t       struct diff_queue_struct *pairs,\n \t\t\t\t       struct hashmap *dir_renames,\n \t\t\t\t       struct hashmap *dir_rename_exclusions,\n@@ -2585,6 +2583,7 @@ static struct string_list *get_renames(struct merge_options *opt,\n \t\tre->processed = 0;\n \t\tre->add_turned_into_rename = 0;\n \t\tre->pair = pair;\n+\t\tre->branch = branch;\n \t\titem = string_list_lookup(entries, re->pair->one->path);\n \t\tif (!item)\n \t\t\tre->src_entry = insert_stage_data(re->pair->one->path,\n@@ -2639,7 +2638,6 @@ static int process_renames(struct merge_options *opt,\n \tfor (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {\n \t\tstruct string_list *renames1, *renames2Dst;\n \t\tstruct rename *ren1 = NULL, *ren2 = NULL;\n-\t\tconst char *branch1, *branch2;\n \t\tconst char *ren1_src, *ren1_dst;\n \t\tstruct string_list_item *lookup;\n \n@@ -2660,13 +2658,9 @@ static int process_renames(struct merge_options *opt,\n \t\tif (ren1) {\n \t\t\trenames1 = a_renames;\n \t\t\trenames2Dst = &b_by_dst;\n-\t\t\tbranch1 = opt->branch1;\n-\t\t\tbranch2 = opt->branch2;\n \t\t} else {\n \t\t\trenames1 = b_renames;\n \t\t\trenames2Dst = &a_by_dst;\n-\t\t\tbranch1 = opt->branch2;\n-\t\t\tbranch2 = opt->branch1;\n \t\t\tSWAP(ren2, ren1);\n \t\t}\n \n@@ -2706,12 +2700,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t     ren1->pair->two,\n \t\t\t\t\t     ren2->pair->two);\n \t\t\t}\n-\t\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t   ren2,\n-\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2);\n+\t\t\tsetup_rename_conflict_info(rename_type, opt, ren1, ren2);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n \t\t\t/* Two different files renamed to the same thing */\n \t\t\tchar *ren2_dst;\n@@ -2730,11 +2719,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\tren2->src_entry->processed = 1;\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n-\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t   ren2,\n-\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t   opt, ren1, ren2);\n \n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n@@ -2769,18 +2754,10 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n \t\t\t    ren1->add_turned_into_rename) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n \t\t\t\t   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {\n \t\t\t\t/*\n@@ -2807,11 +2784,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * file, then the merge will be clean.\n \t\t\t\t */\n \t\t\t\tsetup_rename_conflict_info(RENAME_ADD,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n@@ -2831,11 +2804,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t}\n \t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t}\n \t\t}\n \t}\n@@ -2904,13 +2873,13 @@ static int detect_and_process_renames(struct merge_options *opt,\n \t\tdir_rename_init(dir_re_merge);\n \t}\n \n-\tri->head_renames  = get_renames(opt, head_pairs,\n+\tri->head_renames  = get_renames(opt, opt->branch1, head_pairs,\n \t\t\t\t\tdir_re_merge, dir_re_head, head,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tri->merge_renames = get_renames(opt, merge_pairs,\n+\tri->merge_renames = get_renames(opt, opt->branch2, merge_pairs,\n \t\t\t\t\tdir_re_head, dir_re_merge, merge,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n@@ -3072,14 +3041,14 @@ static int handle_content_merge(struct merge_options *opt,\n \tif (ci) {\n \t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n \n-\t\tpath1 = (opt->branch1 == ci->branch1) ?\n+\t\tpath1 = (opt->branch1 == ci->ren1->branch) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\t/* If ci->ren2->pair != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n \t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n-\t\t\t opt->branch2 == ci->branch1) ?\n+\t\t\t opt->branch2 == ci->ren1->branch) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n@@ -3157,7 +3126,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(opt, path, ci->branch1);\n+\t\tnew_path = unique_path(opt, path, ci->ren1->branch);\n \t\tif (is_dirty) {\n \t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n@@ -3215,7 +3184,8 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair, ci->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair,\n+\t\t\t\t\t\t  ci->ren1->branch))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3230,7 +3200,8 @@ static int process_entry(struct merge_options *opt,\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n \t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n-\t\t\t\t\t\t ci->branch1, ci->branch2))\n+\t\t\t\t\t\t ci->ren1->branch,\n+\t\t\t\t\t\t ci->ren1->branch == opt->branch1 ? opt->branch2 : opt->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372751","messageId":"20190330003336.21940-14-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 13/15] merge-recursive: track information associated with directory renames","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:34Z","receivedAt":"2019-03-30T00:34:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Directory rename detection previously silently applied.  In order to\nallow printing information about paths that changed or printing a\nconflict notification (and only doing so near other potential conflict\nmessages associated with the paths), save this information inside the\nrename struct for later use.  A subsequent patch will make use of the\nadditional information.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1d2c9e1772..938a526b20 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -207,8 +207,16 @@ struct stage_data {\n };\n \n struct rename {\n+\tunsigned processed:1;\n \tstruct diff_filepair *pair;\n \tconst char *branch; /* branch that the rename occurred on */\n+\t/*\n+\t * If directory rename detection affected this rename, what was its\n+\t * original type ('A' or 'R') and it's original destination before\n+\t * the directory rename (otherwise, '\\0' and NULL for these two vars).\n+\t */\n+\tchar dir_rename_original_type;\n+\tchar *dir_rename_original_dest;\n \t/*\n \t * Purpose of src_entry and dst_entry:\n \t *\n@@ -230,8 +238,6 @@ struct rename {\n \t */\n \tstruct stage_data *src_entry;\n \tstruct stage_data *dst_entry;\n-\tunsigned add_turned_into_rename:1;\n-\tunsigned processed:1;\n };\n \n struct rename_conflict_info {\n@@ -2484,16 +2490,18 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t\t       &re->dst_entry->stages[stage].oid,\n \t\t       &re->dst_entry->stages[stage].mode);\n \n-\t/* Update pair status */\n-\tif (pair->status == 'A') {\n-\t\t/*\n-\t\t * Recording rename information for this add makes it look\n-\t\t * like a rename/delete conflict.  Make sure we can\n-\t\t * correctly handle this as an add that was moved to a new\n-\t\t * directory instead of reporting a rename/delete conflict.\n-\t\t */\n-\t\tre->add_turned_into_rename = 1;\n-\t}\n+\t/*\n+\t * Record the original change status (or 'type' of change).  If it\n+\t * was originally an add ('A'), this lets us differentiate later\n+\t * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they\n+\t * otherwise look the same).  If it was originally a rename ('R'),\n+\t * this lets us remember and report accurately about the transitive\n+\t * renaming that occurred via the directory rename detection.  Also,\n+\t * record the original destination name.\n+\t */\n+\tre->dir_rename_original_type = pair->status;\n+\tre->dir_rename_original_dest = pair->two->path;\n+\n \t/*\n \t * We don't actually look at pair->status again, but it seems\n \t * pedagogically correct to adjust it.\n@@ -2556,9 +2564,10 @@ static struct string_list *get_renames(struct merge_options *opt,\n \n \t\tre = xmalloc(sizeof(*re));\n \t\tre->processed = 0;\n-\t\tre->add_turned_into_rename = 0;\n \t\tre->pair = pair;\n \t\tre->branch = branch;\n+\t\tre->dir_rename_original_type = '\\0';\n+\t\tre->dir_rename_original_dest = NULL;\n \t\titem = string_list_lookup(entries, re->pair->one->path);\n \t\tif (!item)\n \t\t\tre->src_entry = insert_stage_data(re->pair->one->path,\n@@ -2726,7 +2735,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\ttry_merge = 0;\n \n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n-\t\t\t    ren1->add_turned_into_rename) {\n+\t\t\t    ren1->dir_rename_original_type == 'A') {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n \t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372753","messageId":"20190330003336.21940-16-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 15/15] merge-recursive: switch directory rename detection default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:36Z","receivedAt":"2019-03-30T00:34:30Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"When all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one\nbranch, there is a question about whether x/d added on a different\nbranch should remain at x/d or appear at z/d when the two branches are\nmerged.  There are different possible viewpoints here:\n\n  A) The file was placed at x/d; it's unrelated to the other files in\n     x/ so it doesn't matter that all the files from x/ moved to z/ on\n     one branch; x/d should still remain at x/d.\n\n  B) x/d is related to the other files in x/, and x/ was renamed to z/;\n     therefore x/d should be moved to z/d.\n\nSince there was no ability to detect directory renames prior to\ngit-2.18, users experienced (A) regardless of context.  Choice (B) was\nimplemented in git-2.18, with no option to go back to (A), and has been\nin use since.  However, one user reported that the merge results did not\nmatch their expectations, making the change of default problematic,\nespecially since there was no notice printed when directory rename\ndetection moved files.\n\nNote that there is also a third possibility here:\n\n  C) There are different answers depending on the context and content\n     that cannot be determined by git, so this is a conflict.  Use a\n     higher stage in the index to record the conflict and notify the\n     user of the potential issue instead of silently selecting a\n     resolution for them.\n\nAdd an option for users to specify their preference for whether to use\ndirectory rename detection, and default to (C).  Even when directory\nrename detection is on, add notice messages about files moved into new\ndirectories.\n\nAs a sidenote, x/d did not have to be a new file here; it could have\nalready existed at some other path and been renamed to x/d, with\ndirectory rename detection just renaming it again to z/d.  Thus, it's\nnot just new files, but also a modification to all rename types (normal\nrenames, rename/add, rename/delete, rename/rename(1to1),\nrename/rename(1to2), and rename/rename(2to1)).\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt         |  19 +-\n merge-recursive.c                      | 151 ++++++--\n t/t3401-rebase-and-am-rename.sh        |   8 +-\n t/t6043-merge-rename-directories.sh    | 458 ++++++++++++++++++++++---\n t/t6046-merge-skip-unneeded-updates.sh |   8 +-\n 5 files changed, 557 insertions(+), 87 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex d389c73929..6a313937f8 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -39,9 +39,22 @@ merge.renameLimit::\n \tis turned off.\n \n merge.renames::\n-\tWhether and how Git detects renames.  If set to \"false\",\n-\trename detection is disabled. If set to \"true\", basic rename\n-\tdetection is enabled.  Defaults to the value of diff.renames.\n+\tWhether Git detects renames.  If set to \"false\", rename detection\n+\tis disabled. If set to \"true\", basic rename detection is enabled.\n+\tDefaults to the value of diff.renames.\n+\n+merge.directoryRenames::\n+\tWhether Git detects directory renames, affecting what happens at\n+\tmerge time to new files added to a directory on one side of\n+\thistory when that directory was renamed on the other side of\n+\thistory.  If merge.directoryRenames is set to \"false\", directory\n+\trename detection is disabled, meaning that such new files will be\n+\tleft behind in the old directory.  If set to \"true\", directory\n+\trename detection is enabled, meaning that such new files will be\n+\tmoved into the new directory.  If set to \"conflict\", a conflict\n+\twill be reported for such paths.  If merge.renames is false,\n+\tmerge.directoryRenames is ignored and treated as false.  Defaults\n+\tto \"conflict\".\n \n merge.renormalize::\n \tTell Git that canonical representation of files in the\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2edfa01e43..a86f37b333 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1370,30 +1370,39 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t */\n \tconst struct rename *ren = ci->ren1;\n \tconst struct diff_filespec *dest = ren->pair->two;\n+\tchar *file_path = dest->path;\n+\tint mark_conflicted = (opt->detect_directory_renames == 1);\n+\tassert(ren->dir_rename_original_dest);\n \n \tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n-\t\tchar *alt_path = unique_path(opt, dest->path, ren->branch);\n-\n+\t\tmark_conflicted = 1;\n+\t\tfile_path = unique_path(opt, dest->path, ren->branch);\n \t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n-\t\t\t       \"writing to %s instead.\"),\n-\t\t       dest->path, alt_path);\n+\t\t\t\t \"writing to %s instead.\"),\n+\t\t       dest->path, file_path);\n+\t}\n+\n+\tif (mark_conflicted) {\n \t\t/*\n-\t\t * Write the file in worktree at alt_path, but not in the\n-\t\t * index.  Instead, write to dest->path for the index but\n-\t\t * only at the higher appropriate stage.\n+\t\t * Write the file in worktree at file_path.  In the index,\n+\t\t * only record the file at dest->path in the appropriate\n+\t\t * higher stage.\n \t\t */\n-\t\tif (update_file(opt, 0, dest, alt_path))\n+\t\tif (update_file(opt, 0, dest, file_path))\n \t\t\treturn -1;\n-\t\tfree(alt_path);\n-\t\treturn update_stages(opt, dest->path, NULL,\n-\t\t\t\t     ren->branch == opt->branch1 ? dest : NULL,\n-\t\t\t\t     ren->branch == opt->branch1 ? NULL : dest);\n+\t\tif (file_path != dest->path)\n+\t\t\tfree(file_path);\n+\t\tif (update_stages(opt, dest->path, NULL,\n+\t\t\t\t  ren->branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t  ren->branch == opt->branch1 ? NULL : dest))\n+\t\t\treturn -1;\n+\t\treturn 0; /* not clean, but conflicted */\n+\t} else {\n+\t\t/* Update dest->path both in index and in worktree */\n+\t\tif (update_file(opt, 1, dest, dest->path))\n+\t\t\treturn -1;\n+\t\treturn 1; /* clean */\n \t}\n-\n-\t/* Update dest->path both in index and in worktree */\n-\tif (update_file(opt, 1, dest, dest->path))\n-\t\treturn -1;\n-\treturn 0;\n }\n \n static int handle_change_delete(struct merge_options *opt,\n@@ -3090,10 +3099,88 @@ static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n-\t/* Merge the content and write it out */\n+\tstruct rename *ren = ci->ren1;\n \tstruct merge_file_info mfi;\n-\treturn handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n-\t\t\t\t    o, a, b, ci);\n+\tint clean;\n+\tint side = (ren->branch == opt->branch1 ? 2 : 3);\n+\n+\t/* Merge the content and write it out */\n+\tclean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n+\t\t\t\t     o, a, b, ci);\n+\n+\tif (clean && opt->detect_directory_renames == 1 &&\n+\t    ren->dir_rename_original_dest) {\n+\t\tif (update_stages(opt, path,\n+\t\t\t\t  NULL,\n+\t\t\t\t  side == 2 ? &mfi.blob : NULL,\n+\t\t\t\t  side == 2 ? NULL : &mfi.blob))\n+\t\t\treturn -1;\n+\t\tclean = 0; /* not clean, but conflicted */\n+\t}\n+\treturn clean;\n+}\n+\n+static void dir_rename_warning(const char *msg,\n+\t\t\t       int is_add,\n+\t\t\t       int clean,\n+\t\t\t       struct merge_options *opt,\n+\t\t\t       struct rename *ren)\n+{\n+\tconst char *other_branch;\n+\tother_branch = (ren->branch == opt->branch1 ?\n+\t\t\topt->branch2 : opt->branch1);\n+\tif (is_add) {\n+\t\toutput(opt, clean ? 2 : 1, msg,\n+\t\t       ren->pair->one->path, ren->branch,\n+\t\t       other_branch, ren->pair->two->path);\n+\t\treturn;\n+\t}\n+\toutput(opt, clean ? 2 : 1, msg,\n+\t       ren->pair->one->path, ren->dir_rename_original_dest, ren->branch,\n+\t       other_branch, ren->pair->two->path);\n+}\n+static int warn_about_dir_renamed_entries(struct merge_options *opt,\n+\t\t\t\t\t  struct rename *ren)\n+{\n+\tconst char *msg;\n+\tint clean = 1, is_add;\n+\n+\tif (!ren)\n+\t\treturn clean;\n+\n+\t/* Return early if ren was not affected/created by a directory rename */\n+\tif (!ren->dir_rename_original_dest)\n+\t\treturn clean;\n+\n+\t/* Sanity checks */\n+\tassert(opt->detect_directory_renames > 0);\n+\tassert(ren->dir_rename_original_type == 'A' ||\n+\t       ren->dir_rename_original_type == 'R');\n+\n+\t/* Check whether to treat directory renames as a conflict */\n+\tclean = (opt->detect_directory_renames == 2);\n+\n+\tis_add = (ren->dir_rename_original_type == 'A');\n+\tif (ren->dir_rename_original_type == 'A' && clean) {\n+\t\tmsg = _(\"Path updated: %s added in %s inside a \"\n+\t\t\t\"directory that was renamed in %s; moving it to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'A' && !clean) {\n+\t\tmsg = _(\"CONFLICT (file location): %s added in %s \"\n+\t\t\t\"inside a directory that was renamed in %s, \"\n+\t\t\t\"suggesting it should perhaps be moved to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'R' && clean) {\n+\t\tmsg = _(\"Path updated: %s renamed to %s in %s, inside a \"\n+\t\t\t\"directory that was renamed in %s; moving it to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'R' && !clean) {\n+\t\tmsg = _(\"CONFLICT (file location): %s renamed to %s in %s, \"\n+\t\t\t\"inside a directory that was renamed in %s, \"\n+\t\t\t\"suggesting it should perhaps be moved to %s.\");\n+\t} else {\n+\t\tBUG(\"Impossible dir_rename_original_type/clean combination\");\n+\t}\n+\tdir_rename_warning(msg, is_add, clean, opt, ren);\n+\n+\treturn clean;\n }\n \n /* Per entry merge function */\n@@ -3115,6 +3202,10 @@ static int process_entry(struct merge_options *opt,\n \tif (entry->rename_conflict_info) {\n \t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n \t\tstruct diff_filespec *temp;\n+\t\tint path_clean;\n+\n+\t\tpath_clean = warn_about_dir_renamed_entries(opt, ci->ren1);\n+\t\tpath_clean &= warn_about_dir_renamed_entries(opt, ci->ren2);\n \n \t\t/*\n \t\t * For cases with a single rename, {o,a,b}->path have all been\n@@ -3135,9 +3226,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n-\t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci))\n-\t\t\t\tclean_merge = -1;\n+\t\t\tclean_merge = handle_rename_via_dir(opt, ci);\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n \t\t\t/*\n@@ -3187,6 +3276,8 @@ static int process_entry(struct merge_options *opt,\n \t\t\tentry->processed = 0;\n \t\t\tbreak;\n \t\t}\n+\t\tif (path_clean < clean_merge)\n+\t\t\tclean_merge = path_clean;\n \t} else if (o_valid && (!a_valid || !b_valid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_valid && !b_valid) ||\n@@ -3558,6 +3649,20 @@ static void merge_recursive_config(struct merge_options *opt)\n \t\topt->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n \t\tfree(value);\n \t}\n+\tif (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n+\t\tif (!strcasecmp(value, \"true\"))\n+\t\t\topt->detect_directory_renames = 2;\n+\t\telse if (!strcasecmp(value, \"false\"))\n+\t\t\topt->detect_directory_renames = 0;\n+\t\telse if (!strcasecmp(value, \"conflict\"))\n+\t\t\topt->detect_directory_renames = 1;\n+\t\telse {\n+\t\t\terror(_(\"Invalid value for merge.directoryRenames: %s\"),\n+\t\t\t      value);\n+\t\t\topt->detect_directory_renames = 1;\n+\t\t}\n+\t\tfree(value);\n+\t}\n \tgit_config(git_xmerge_config, NULL);\n }\n \ndiff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh\nindex e0b5111993..a0b9438b22 100755\n--- a/t/t3401-rebase-and-am-rename.sh\n+++ b/t/t3401-rebase-and-am-rename.sh\n@@ -42,7 +42,7 @@ test_expect_success 'rebase --interactive: directory rename detected' '\n \t\tgit checkout B^0 &&\n \n \t\tset_fake_editor &&\n-\t\tFAKE_LINES=\"1\" git rebase --interactive A &&\n+\t\tFAKE_LINES=\"1\" git -c merge.directoryRenames=true rebase --interactive A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -58,7 +58,7 @@ test_expect_failure 'rebase (am): directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase A &&\n+\t\tgit -c merge.directoryRenames=true rebase A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -74,7 +74,7 @@ test_expect_success 'rebase --merge: directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase --merge A &&\n+\t\tgit -c merge.directoryRenames=true rebase --merge A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -92,7 +92,7 @@ test_expect_failure 'am: directory rename detected' '\n \n \t\tgit format-patch -1 B &&\n \n-\t\tgit am --3way 0001*.patch &&\n+\t\tgit -c merge.directoryRenames=true am --3way 0001*.patch &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\ndiff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh\nindex fe205be607..50b7543483 100755\n--- a/t/t6043-merge-rename-directories.sh\n+++ b/t/t6043-merge-rename-directories.sh\n@@ -75,7 +75,7 @@ test_expect_success '1a-check: Simple directory rename detection' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -142,7 +142,7 @@ test_expect_success '1b-check: Merge a directory with another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -201,7 +201,7 @@ test_expect_success '1c-check: Transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -270,7 +270,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -350,7 +350,7 @@ test_expect_success '1e-check: Renamed directory, with all files being renamed t\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -416,7 +416,7 @@ test_expect_success '1f-check: Split a directory into two other directories' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -497,7 +497,7 @@ test_expect_success '2a-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*directory rename split\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -559,7 +559,7 @@ test_expect_success '2b-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -640,7 +640,7 @@ test_expect_success '3a-check: Avoid implicit rename if involved as source on ot\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -705,7 +705,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&\n \t\ttest_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&\n \n@@ -826,7 +826,7 @@ test_expect_success '4a-check: Directory split, with original directory still pr\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -915,7 +915,7 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*implicit dir rename\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -989,7 +989,7 @@ test_expect_success '5b-check: Rename/delete in order to get add/add/add conflic\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1069,7 +1069,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*z/d\" out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n@@ -1153,7 +1153,7 @@ test_expect_success '5d-check: Directory/file/file conflict due to directory ren\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (file/directory).*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1243,7 +1243,7 @@ test_expect_success '6a-check: Tricky rename/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*z/c.*y/c\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1308,7 +1308,7 @@ test_expect_success '6b-check: Same rename done on both sides' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1370,7 +1370,7 @@ test_expect_success '6c-check: Rename only done on same side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1432,7 +1432,7 @@ test_expect_success '6d-check: We do not always want transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1495,7 +1495,7 @@ test_expect_success '6e-check: Add/add from one side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -1591,7 +1591,7 @@ test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/b.*y/b.*w/b\" out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/c.*y/c.*x/c\" out &&\n \n@@ -1663,7 +1663,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1740,7 +1740,7 @@ test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1804,7 +1804,7 @@ test_expect_success '7d-check: transitive rename involved in rename/delete; how\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1894,7 +1894,7 @@ test_expect_success '7e-check: transitive rename in rename/delete AND dirs in th\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1985,7 +1985,7 @@ test_expect_success '8a-check: Dual-directory rename, one into the others way' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2063,7 +2063,7 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2135,7 +2135,7 @@ test_expect_success '8c-check: modify/delete or rename+modify/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (modify/delete).* z/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2212,7 +2212,7 @@ test_expect_success '8d-check: rename/delete...or not?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2287,7 +2287,7 @@ test_expect_success '8e-check: Both sides rename, one side adds to original dire\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&\n \n@@ -2374,7 +2374,7 @@ test_expect_success '9a-check: Inner renamed directory within outer renamed dire\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 7 out &&\n@@ -2444,7 +2444,7 @@ test_expect_success '9b-check: Transitive rename with content merge' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2534,7 +2534,7 @@ test_expect_success '9c-check: Doubly transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> z rename to x/f\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2622,7 +2622,7 @@ test_expect_success '9d-check: N-way transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying z -> y rename to z/t\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying y -> x rename to y/a\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> w rename to x/b\" out &&\n@@ -2704,7 +2704,7 @@ test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\tgrep \"CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo\" out >error_line &&\n \t\tgrep -q dir1/yo error_line &&\n \t\tgrep -q dir2/yo error_line &&\n@@ -2782,7 +2782,7 @@ test_expect_success '9f-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2849,7 +2849,7 @@ test_expect_failure '9g-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2918,7 +2918,7 @@ test_expect_success '9h-check: Avoid dir rename on merely modified path' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2993,7 +2993,7 @@ test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '\n \t\techo very >z/c &&\n \t\techo important >z/d &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"The following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3061,7 +3061,7 @@ test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '\n \t\techo important >y/d &&\n \t\techo contents >y/e &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*Version B\\^0 of y/d left in tree at y/d~B\\^0\" out &&\n \t\ttest_i18ngrep \"Error: Refusing to lose untracked file at y/e; writing to y/e~B\\^0 instead\" out &&\n \n@@ -3137,7 +3137,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tgit checkout A^0 &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~B\\^0 instead\" out &&\n \n@@ -3174,7 +3174,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tmkdir y &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive A^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~HEAD instead\" out &&\n \n@@ -3249,7 +3249,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '\n \t\tgit checkout A^0 &&\n \t\techo important >y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/wham\" out &&\n \n@@ -3327,7 +3327,7 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n\n \t\tmkdir z &&\n \t\techo random >z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep ! \"following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3407,7 +3407,7 @@ test_expect_success '11a-check: Avoid losing dirty contents with simple rename'\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\ttest_seq 1 10 >expected &&\n@@ -3479,7 +3479,7 @@ test_expect_success '11b-check: Avoid losing dirty file involved in directory re\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3554,7 +3554,7 @@ test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"following files would be overwritten by merge\" err &&\n \n \t\tgrep -q stuff y/c &&\n@@ -3621,7 +3621,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3700,7 +3700,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo mods >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/c\" out &&\n \n@@ -3782,7 +3782,7 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo important >>y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/wham\" out &&\n \n@@ -3870,7 +3870,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -3946,7 +3946,7 @@ test_expect_success '12b-check: Moving two directory hierarchies into each other\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -4016,7 +4016,7 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -u >out &&\n \t\ttest_line_count = 12 out &&\n@@ -4051,4 +4051,356 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \t)\n '\n \n+###########################################################################\n+# SECTION 13: Checking informational and conflict messages\n+#\n+# A year after directory rename detection became the default, it was\n+# instead decided to report conflicts on the pathname on the basis that\n+# some users may expect the new files added or moved into a directory to\n+# be unrelated to all the other files in that directory, and thus that\n+# directory rename detection is unexpected.  Test that the messages printed\n+# match our expectation.\n+###########################################################################\n+\n+# Testcase 13a, Basic directory rename with newly added files\n+#   Commit O: z/{b,c}\n+#   Commit A: y/{b,c}\n+#   Commit B: z/{b,c,d,e/f}\n+#   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f\n+\n+test_expect_success '13a-setup: messages for newly added files' '\n+\ttest_create_repo 13a &&\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tmkdir z &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo d >z/d &&\n+\t\tmkdir z/e &&\n+\t\techo f >z/e/f &&\n+\t\tgit add z/d z/e/f &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13a-check(conflict): messages for newly added files' '\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/[de]\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d &&\n+\t\ttest_path_is_missing z/e/f &&\n+\t\ttest_path_is_file    y/e/f\n+\t)\n+'\n+\n+test_expect_success '13a-check(info): messages for newly added files' '\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&\n+\t\ttest_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/[de]\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d &&\n+\t\ttest_path_is_missing z/e/f &&\n+\t\ttest_path_is_file    y/e/f\n+\t)\n+'\n+\n+# Testcase 13b, Transitive rename with conflicted content merge and default\n+#               \"conflict\" setting\n+#   (Related to testcase 1c, 9b)\n+#   Commit O: z/{b,c},   x/d_1\n+#   Commit A: y/{b,c},   x/d_2\n+#   Commit B: z/{b,c,d_3}\n+#   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,\n+#             one about content, and one about file location\n+\n+test_expect_success '13b-setup: messages for transitive rename with conflicted content' '\n+\ttest_create_repo 13b &&\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tmkdir x &&\n+\t\tmkdir z &&\n+\t\ttest_seq 1 10 >x/d &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add x z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\techo 11 >>x/d &&\n+\t\tgit add x/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo eleven >>x/d &&\n+\t\tgit mv x/d z/d &&\n+\t\tgit add z/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&\n+\t\ttest_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+# Testcase 13c, Rename/rename(1to1) due to directory rename\n+#   Commit O: z/{b,c},   x/{d,e}\n+#   Commit A: y/{b,c,d}, x/e\n+#   Commit B: z/{b,c,d}, x/e\n+#   Expected: y/{b,c,d}, with info or conflict messages for d (\n+#             A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d\n+#             One could argue A had partial knowledge of what was done with\n+#             d and B had full knowledge, but that's a slippery slope as\n+#             shown in testcase 13d.\n+\n+test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '\n+\ttest_create_repo 13c &&\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tmkdir x &&\n+\t\tmkdir z &&\n+\t\ttest_seq 1 10 >x/d &&\n+\t\techo e >x/e &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add x z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\tgit mv x/d y/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv x/d z/d &&\n+\t\tgit add z/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides\n+#   Commit O: a/{z,y}, b/x,     c/w\n+#   Commit A: a/z,     b/{y,x}, d/w\n+#   Commit B: a/z,     d/x,     c/{y,w}\n+#   Expected: a/z, d/{y,x,w} with no file location conflict for x\n+#             Easy cases:\n+#               * z is always in a; so it stays in a.\n+#               * x starts in b, only modified on one side to move into d/\n+#               * w starts in c, only modified on one side to move into d/\n+#             Hard case:\n+#               * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y\n+#               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y\n+#               No conflict in where a/y ends up, so put it in d/y.\n+\n+test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '\n+\ttest_create_repo 13d &&\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tmkdir a &&\n+\t\tmkdir b &&\n+\t\tmkdir c &&\n+\t\techo z >a/z &&\n+\t\techo y >a/y &&\n+\t\techo x >b/x &&\n+\t\techo w >c/w &&\n+\t\tgit add a b c &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv a/y b/ &&\n+\t\tgit mv c/ d/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv a/y c/ &&\n+\t\tgit mv b/ d/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep b/ paths &&\n+\t\t! grep c/ paths &&\n+\t\tgrep \"d/y\" paths &&\n+\n+\t\ttest_path_is_missing b/y &&\n+\t\ttest_path_is_missing c/y &&\n+\t\ttest_path_is_file    d/y\n+\t)\n+'\n+\n+test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&\n+\t\ttest_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep b/ paths &&\n+\t\t! grep c/ paths &&\n+\t\tgrep \"d/y\" paths &&\n+\n+\t\ttest_path_is_missing b/y &&\n+\t\ttest_path_is_missing c/y &&\n+\t\ttest_path_is_file    d/y\n+\t)\n+'\n+\n test_done\ndiff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh\nindex 38e24f787c..3a47623ed3 100755\n--- a/t/t6046-merge-skip-unneeded-updates.sh\n+++ b/t/t6046-merge-skip-unneeded-updates.sh\n@@ -466,7 +466,7 @@ test_expect_success '3a-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -495,7 +495,7 @@ test_expect_success '3a-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -560,7 +560,7 @@ test_expect_success '3b-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -589,7 +589,7 @@ test_expect_success '3b-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372752","messageId":"20190330003336.21940-3-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 02/15] merge-recursive: rename merge_options argument from 'o' to 'opt'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:23Z","receivedAt":"2019-03-30T00:34:31Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The name 'o' was used for the merge_options struct pointer taken by many\nfunctions, but in a few places it was named 'opt'.  Several functions\nthat didn't need merge_options instead used 'o' for a diff_filespec\nargument or local.  Some functions needed both an inconsistently either\nrenamed the merge_options to 'opt' or the diff_filespec to 'one'.  I\nwant to remove the weird split in the codebase between using a\ndiff_filespec and a pair of (oid,mode) values in favor of using a\ndiff_filespec everywhere, but that dramatically increases the number of\ncases where we want to use 'o' as a diff_filespec.  Rename the\nmerge_options argument to 'opt' to make room.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 984 +++++++++++++++++++++++-----------------------\n 1 file changed, 492 insertions(+), 492 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fcf37be2a7..09b76d596e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -115,32 +115,32 @@ static void collision_init(struct hashmap *map)\n \thashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);\n }\n \n-static void flush_output(struct merge_options *o)\n+static void flush_output(struct merge_options *opt)\n {\n-\tif (o->buffer_output < 2 && o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n+\tif (opt->buffer_output < 2 && opt->obuf.len) {\n+\t\tfputs(opt->obuf.buf, stdout);\n+\t\tstrbuf_reset(&opt->obuf);\n \t}\n }\n \n-static int err(struct merge_options *o, const char *err, ...)\n+static int err(struct merge_options *opt, const char *err, ...)\n {\n \tva_list params;\n \n-\tif (o->buffer_output < 2)\n-\t\tflush_output(o);\n+\tif (opt->buffer_output < 2)\n+\t\tflush_output(opt);\n \telse {\n-\t\tstrbuf_complete(&o->obuf, '\\n');\n-\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t\tstrbuf_complete(&opt->obuf, '\\n');\n+\t\tstrbuf_addstr(&opt->obuf, \"error: \");\n \t}\n \tva_start(params, err);\n-\tstrbuf_vaddf(&o->obuf, err, params);\n+\tstrbuf_vaddf(&opt->obuf, err, params);\n \tva_end(params);\n-\tif (o->buffer_output > 1)\n-\t\tstrbuf_addch(&o->obuf, '\\n');\n+\tif (opt->buffer_output > 1)\n+\t\tstrbuf_addch(&opt->obuf, '\\n');\n \telse {\n-\t\terror(\"%s\", o->obuf.buf);\n-\t\tstrbuf_reset(&o->obuf);\n+\t\terror(\"%s\", opt->obuf.buf);\n+\t\tstrbuf_reset(&opt->obuf);\n \t}\n \n \treturn -1;\n@@ -228,7 +228,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      const char *branch2,\n \t\t\t\t\t      struct stage_data *dst_entry1,\n \t\t\t\t\t      struct stage_data *dst_entry2,\n-\t\t\t\t\t      struct merge_options *o,\n+\t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct stage_data *src_entry1,\n \t\t\t\t\t      struct stage_data *src_entry2)\n {\n@@ -239,15 +239,15 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * When we have two renames involved, it's easiest to get the\n \t * correct things into stage 2 and 3, and to make sure that the\n \t * content merge puts HEAD before the other branch if we just\n-\t * ensure that branch1 == o->branch1.  So, simply flip arguments\n+\t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (dst_entry2 && branch1 != o->branch1) {\n+\tif (dst_entry2 && branch1 != opt->branch1) {\n \t\tsetup_rename_conflict_info(rename_type,\n \t\t\t\t\t   pair2,      pair1,\n \t\t\t\t\t   branch2,    branch1,\n \t\t\t\t\t   dst_entry2, dst_entry1,\n-\t\t\t\t\t   o,\n+\t\t\t\t\t   opt,\n \t\t\t\t\t   src_entry2, src_entry1);\n \t\treturn;\n \t}\n@@ -276,7 +276,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t */\n \tif (rename_type == RENAME_ADD ||\n \t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage1 = o->branch1 == branch1 ? 3 : 2;\n+\t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n \n \t\tci->ren1_other.path = pair1->one->path;\n \t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n@@ -292,67 +292,67 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t}\n }\n \n-static int show(struct merge_options *o, int v)\n+static int show(struct merge_options *opt, int v)\n {\n-\treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n+\treturn (!opt->call_depth && opt->verbosity >= v) || opt->verbosity >= 5;\n }\n \n __attribute__((format (printf, 3, 4)))\n-static void output(struct merge_options *o, int v, const char *fmt, ...)\n+static void output(struct merge_options *opt, int v, const char *fmt, ...)\n {\n \tva_list ap;\n \n-\tif (!show(o, v))\n+\tif (!show(opt, v))\n \t\treturn;\n \n-\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n+\tstrbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);\n \n \tva_start(ap, fmt);\n-\tstrbuf_vaddf(&o->obuf, fmt, ap);\n+\tstrbuf_vaddf(&opt->obuf, fmt, ap);\n \tva_end(ap);\n \n-\tstrbuf_addch(&o->obuf, '\\n');\n-\tif (!o->buffer_output)\n-\t\tflush_output(o);\n+\tstrbuf_addch(&opt->obuf, '\\n');\n+\tif (!opt->buffer_output)\n+\t\tflush_output(opt);\n }\n \n-static void output_commit_title(struct merge_options *o, struct commit *commit)\n+static void output_commit_title(struct merge_options *opt, struct commit *commit)\n {\n \tstruct merge_remote_desc *desc;\n \n-\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n+\tstrbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);\n \tdesc = merge_remote_util(commit);\n \tif (desc)\n-\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\", desc->name);\n+\t\tstrbuf_addf(&opt->obuf, \"virtual %s\\n\", desc->name);\n \telse {\n-\t\tstrbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,\n+\t\tstrbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,\n \t\t\t\t\t DEFAULT_ABBREV);\n-\t\tstrbuf_addch(&o->obuf, ' ');\n+\t\tstrbuf_addch(&opt->obuf, ' ');\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tstrbuf_addstr(&o->obuf, _(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addstr(&opt->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&opt->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n-\tflush_output(o);\n+\tflush_output(opt);\n }\n \n-static int add_cacheinfo(struct merge_options *o,\n+static int add_cacheinfo(struct merge_options *opt,\n \t\t\t unsigned int mode, const struct object_id *oid,\n \t\t\t const char *path, int stage, int refresh, int options)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \tstruct cache_entry *ce;\n \tint ret;\n \n \tce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);\n \tif (!ce)\n-\t\treturn err(o, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n+\t\treturn err(opt, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n \n \tret = add_index_entry(istate, ce, options);\n \tif (refresh) {\n@@ -361,7 +361,7 @@ static int add_cacheinfo(struct merge_options *o,\n \t\tnce = refresh_cache_entry(istate, ce,\n \t\t\t\t\t  CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);\n \t\tif (!nce)\n-\t\t\treturn err(o, _(\"add_cacheinfo failed to refresh for path '%s'; merge aborting.\"), path);\n+\t\t\treturn err(opt, _(\"add_cacheinfo failed to refresh for path '%s'; merge aborting.\"), path);\n \t\tif (nce != ce)\n \t\t\tret = add_index_entry(istate, nce, options);\n \t}\n@@ -374,7 +374,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)\n \tinit_tree_desc(desc, tree->buffer, tree->size);\n }\n \n-static int unpack_trees_start(struct merge_options *o,\n+static int unpack_trees_start(struct merge_options *opt,\n \t\t\t      struct tree *common,\n \t\t\t      struct tree *head,\n \t\t\t      struct tree *merge)\n@@ -383,49 +383,49 @@ static int unpack_trees_start(struct merge_options *o,\n \tstruct tree_desc t[3];\n \tstruct index_state tmp_index = { NULL };\n \n-\tmemset(&o->unpack_opts, 0, sizeof(o->unpack_opts));\n-\tif (o->call_depth)\n-\t\to->unpack_opts.index_only = 1;\n+\tmemset(&opt->unpack_opts, 0, sizeof(opt->unpack_opts));\n+\tif (opt->call_depth)\n+\t\topt->unpack_opts.index_only = 1;\n \telse\n-\t\to->unpack_opts.update = 1;\n-\to->unpack_opts.merge = 1;\n-\to->unpack_opts.head_idx = 2;\n-\to->unpack_opts.fn = threeway_merge;\n-\to->unpack_opts.src_index = o->repo->index;\n-\to->unpack_opts.dst_index = &tmp_index;\n-\to->unpack_opts.aggressive = !merge_detect_rename(o);\n-\tsetup_unpack_trees_porcelain(&o->unpack_opts, \"merge\");\n+\t\topt->unpack_opts.update = 1;\n+\topt->unpack_opts.merge = 1;\n+\topt->unpack_opts.head_idx = 2;\n+\topt->unpack_opts.fn = threeway_merge;\n+\topt->unpack_opts.src_index = opt->repo->index;\n+\topt->unpack_opts.dst_index = &tmp_index;\n+\topt->unpack_opts.aggressive = !merge_detect_rename(opt);\n+\tsetup_unpack_trees_porcelain(&opt->unpack_opts, \"merge\");\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n \tinit_tree_desc_from_tree(t+2, merge);\n \n-\trc = unpack_trees(3, t, &o->unpack_opts);\n-\tcache_tree_free(&o->repo->index->cache_tree);\n+\trc = unpack_trees(3, t, &opt->unpack_opts);\n+\tcache_tree_free(&opt->repo->index->cache_tree);\n \n \t/*\n-\t * Update o->repo->index to match the new results, AFTER saving a copy\n-\t * in o->orig_index.  Update src_index to point to the saved copy.\n+\t * Update opt->repo->index to match the new results, AFTER saving a copy\n+\t * in opt->orig_index.  Update src_index to point to the saved copy.\n \t * (verify_uptodate() checks src_index, and the original index is\n \t * the one that had the necessary modification timestamps.)\n \t */\n-\to->orig_index = *o->repo->index;\n-\t*o->repo->index = tmp_index;\n-\to->unpack_opts.src_index = &o->orig_index;\n+\topt->orig_index = *opt->repo->index;\n+\t*opt->repo->index = tmp_index;\n+\topt->unpack_opts.src_index = &opt->orig_index;\n \n \treturn rc;\n }\n \n-static void unpack_trees_finish(struct merge_options *o)\n+static void unpack_trees_finish(struct merge_options *opt)\n {\n-\tdiscard_index(&o->orig_index);\n-\tclear_unpack_trees_porcelain(&o->unpack_opts);\n+\tdiscard_index(&opt->orig_index);\n+\tclear_unpack_trees_porcelain(&opt->unpack_opts);\n }\n \n-struct tree *write_tree_from_memory(struct merge_options *o)\n+struct tree *write_tree_from_memory(struct merge_options *opt)\n {\n \tstruct tree *result = NULL;\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \n \tif (unmerged_index(istate)) {\n \t\tint i;\n@@ -444,11 +444,11 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(istate->cache_tree) &&\n \t    cache_tree_update(istate, 0) < 0) {\n-\t\terr(o, _(\"error building trees\"));\n+\t\terr(opt, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n-\tresult = lookup_tree(o->repo, &istate->cache_tree->oid);\n+\tresult = lookup_tree(opt->repo, &istate->cache_tree->oid);\n \n \treturn result;\n }\n@@ -459,24 +459,24 @@ static int save_files_dirs(const struct object_id *oid,\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n-\tstruct merge_options *o = context;\n+\tstruct merge_options *opt = context;\n \n \tstrbuf_addstr(base, path);\n \n \tFLEX_ALLOC_MEM(entry, path, base->buf, base->len);\n \thashmap_entry_init(entry, path_hash(entry->path));\n-\thashmap_add(&o->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, entry);\n \n \tstrbuf_setlen(base, baselen);\n \treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n }\n \n-static void get_files_dirs(struct merge_options *o, struct tree *tree)\n+static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n \tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n-\t\t\t    &match_all, save_files_dirs, o);\n+\t\t\t    &match_all, save_files_dirs, opt);\n }\n \n static int get_tree_entry_if_blob(const struct object_id *tree,\n@@ -573,7 +573,7 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \treturn onelen - twolen;\n }\n \n-static void record_df_conflict_files(struct merge_options *o,\n+static void record_df_conflict_files(struct merge_options *opt,\n \t\t\t\t     struct string_list *entries)\n {\n \t/* If there is a D/F conflict and the file for such a conflict\n@@ -598,7 +598,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \t * If we're merging merge-bases, we don't want to bother with\n \t * any working directory changes.\n \t */\n-\tif (o->call_depth)\n+\tif (opt->call_depth)\n \t\treturn;\n \n \t/* Ensure D/F conflicts are adjacent in the entries list. */\n@@ -610,7 +610,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \tdf_sorted_entries.cmp = string_list_df_name_compare;\n \tstring_list_sort(&df_sorted_entries);\n \n-\tstring_list_clear(&o->df_conflict_file_set, 1);\n+\tstring_list_clear(&opt->df_conflict_file_set, 1);\n \tfor (i = 0; i < df_sorted_entries.nr; i++) {\n \t\tconst char *path = df_sorted_entries.items[i].string;\n \t\tint len = strlen(path);\n@@ -626,7 +626,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \t\t    len > last_len &&\n \t\t    memcmp(path, last_file, last_len) == 0 &&\n \t\t    path[last_len] == '/') {\n-\t\t\tstring_list_insert(&o->df_conflict_file_set, last_file);\n+\t\t\tstring_list_insert(&opt->df_conflict_file_set, last_file);\n \t\t}\n \n \t\t/*\n@@ -717,20 +717,20 @@ static void update_entry(struct stage_data *entry,\n \toidcpy(&entry->stages[3].oid, &b->oid);\n }\n \n-static int remove_file(struct merge_options *o, int clean,\n+static int remove_file(struct merge_options *opt, int clean,\n \t\t       const char *path, int no_wd)\n {\n-\tint update_cache = o->call_depth || clean;\n-\tint update_working_directory = !o->call_depth && !no_wd;\n+\tint update_cache = opt->call_depth || clean;\n+\tint update_working_directory = !opt->call_depth && !no_wd;\n \n \tif (update_cache) {\n-\t\tif (remove_file_from_index(o->repo->index, path))\n+\t\tif (remove_file_from_index(opt->repo->index, path))\n \t\t\treturn -1;\n \t}\n \tif (update_working_directory) {\n \t\tif (ignore_case) {\n \t\t\tstruct cache_entry *ce;\n-\t\t\tce = index_file_exists(o->repo->index, path, strlen(path),\n+\t\t\tce = index_file_exists(opt->repo->index, path, strlen(path),\n \t\t\t\t\t       ignore_case);\n \t\t\tif (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))\n \t\t\t\treturn 0;\n@@ -751,7 +751,7 @@ static void add_flattened_path(struct strbuf *out, const char *s)\n \t\t\tout->buf[i] = '_';\n }\n \n-static char *unique_path(struct merge_options *o, const char *path, const char *branch)\n+static char *unique_path(struct merge_options *opt, const char *path, const char *branch)\n {\n \tstruct path_hashmap_entry *entry;\n \tstruct strbuf newpath = STRBUF_INIT;\n@@ -762,16 +762,16 @@ static char *unique_path(struct merge_options *o, const char *path, const char *\n \tadd_flattened_path(&newpath, branch);\n \n \tbase_len = newpath.len;\n-\twhile (hashmap_get_from_hash(&o->current_file_dir_set,\n+\twhile (hashmap_get_from_hash(&opt->current_file_dir_set,\n \t\t\t\t     path_hash(newpath.buf), newpath.buf) ||\n-\t       (!o->call_depth && file_exists(newpath.buf))) {\n+\t       (!opt->call_depth && file_exists(newpath.buf))) {\n \t\tstrbuf_setlen(&newpath, base_len);\n \t\tstrbuf_addf(&newpath, \"_%d\", suffix++);\n \t}\n \n \tFLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);\n \thashmap_entry_init(entry, path_hash(entry->path));\n-\thashmap_add(&o->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, entry);\n \treturn strbuf_detach(&newpath, NULL);\n }\n \n@@ -810,10 +810,10 @@ static int dir_in_way(struct index_state *istate, const char *path,\n  * Returns whether path was tracked in the index before the merge started,\n  * and its oid and mode match the specified values\n  */\n-static int was_tracked_and_matches(struct merge_options *o, const char *path,\n+static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n \t\t\t\t   const struct object_id *oid, unsigned mode)\n {\n-\tint pos = index_name_pos(&o->orig_index, path, strlen(path));\n+\tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \tstruct cache_entry *ce;\n \n \tif (0 > pos)\n@@ -821,16 +821,16 @@ static int was_tracked_and_matches(struct merge_options *o, const char *path,\n \t\treturn 0;\n \n \t/* See if the file we were tracking before matches */\n-\tce = o->orig_index.cache[pos];\n+\tce = opt->orig_index.cache[pos];\n \treturn (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);\n }\n \n /*\n  * Returns whether path was tracked in the index before the merge started\n  */\n-static int was_tracked(struct merge_options *o, const char *path)\n+static int was_tracked(struct merge_options *opt, const char *path)\n {\n-\tint pos = index_name_pos(&o->orig_index, path, strlen(path));\n+\tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \n \tif (0 <= pos)\n \t\t/* we were tracking this path before the merge */\n@@ -839,13 +839,13 @@ static int was_tracked(struct merge_options *o, const char *path)\n \treturn 0;\n }\n \n-static int would_lose_untracked(struct merge_options *o, const char *path)\n+static int would_lose_untracked(struct merge_options *opt, const char *path)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \n \t/*\n \t * This may look like it can be simplified to:\n-\t *   return !was_tracked(o, path) && file_exists(path)\n+\t *   return !was_tracked(opt, path) && file_exists(path)\n \t * but it can't.  This function needs to know whether path was in\n \t * the working tree due to EITHER having been tracked in the index\n \t * before the merge OR having been put into the working copy and\n@@ -882,38 +882,38 @@ static int would_lose_untracked(struct merge_options *o, const char *path)\n \treturn file_exists(path);\n }\n \n-static int was_dirty(struct merge_options *o, const char *path)\n+static int was_dirty(struct merge_options *opt, const char *path)\n {\n \tstruct cache_entry *ce;\n \tint dirty = 1;\n \n-\tif (o->call_depth || !was_tracked(o, path))\n+\tif (opt->call_depth || !was_tracked(opt, path))\n \t\treturn !dirty;\n \n-\tce = index_file_exists(o->unpack_opts.src_index,\n+\tce = index_file_exists(opt->unpack_opts.src_index,\n \t\t\t       path, strlen(path), ignore_case);\n-\tdirty = verify_uptodate(ce, &o->unpack_opts) != 0;\n+\tdirty = verify_uptodate(ce, &opt->unpack_opts) != 0;\n \treturn dirty;\n }\n \n-static int make_room_for_path(struct merge_options *o, const char *path)\n+static int make_room_for_path(struct merge_options *opt, const char *path)\n {\n \tint status, i;\n \tconst char *msg = _(\"failed to create path '%s'%s\");\n \n \t/* Unlink any D/F conflict files that are in the way */\n-\tfor (i = 0; i < o->df_conflict_file_set.nr; i++) {\n-\t\tconst char *df_path = o->df_conflict_file_set.items[i].string;\n+\tfor (i = 0; i < opt->df_conflict_file_set.nr; i++) {\n+\t\tconst char *df_path = opt->df_conflict_file_set.items[i].string;\n \t\tsize_t pathlen = strlen(path);\n \t\tsize_t df_pathlen = strlen(df_path);\n \t\tif (df_pathlen < pathlen &&\n \t\t    path[df_pathlen] == '/' &&\n \t\t    strncmp(path, df_path, df_pathlen) == 0) {\n-\t\t\toutput(o, 3,\n+\t\t\toutput(opt, 3,\n \t\t\t       _(\"Removing %s to make room for subdirectory\\n\"),\n \t\t\t       df_path);\n \t\t\tunlink(df_path);\n-\t\t\tunsorted_string_list_delete_item(&o->df_conflict_file_set,\n+\t\t\tunsorted_string_list_delete_item(&opt->df_conflict_file_set,\n \t\t\t\t\t\t\t i, 0);\n \t\t\tbreak;\n \t\t}\n@@ -924,16 +924,16 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn err(o, msg, path, \"\");\n+\t\t\treturn err(opt, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(opt, msg, path, \"\");\n \t}\n \n \t/*\n \t * Do not unlink a file in the work tree if we are not\n \t * tracking it.\n \t */\n-\tif (would_lose_untracked(o, path))\n-\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n+\tif (would_lose_untracked(opt, path))\n+\t\treturn err(opt, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t   path);\n \n \t/* Successful unlink is good.. */\n@@ -943,10 +943,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(opt, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static int update_file_flags(struct merge_options *o,\n+static int update_file_flags(struct merge_options *opt,\n \t\t\t     const struct object_id *oid,\n \t\t\t     unsigned mode,\n \t\t\t     const char *path,\n@@ -955,7 +955,7 @@ static int update_file_flags(struct merge_options *o,\n {\n \tint ret = 0;\n \n-\tif (o->call_depth)\n+\tif (opt->call_depth)\n \t\tupdate_wd = 0;\n \n \tif (update_wd) {\n@@ -975,21 +975,21 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_object_file(oid, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n-\t\t\tif (convert_to_working_tree(o->repo->index, path, buf, size, &strbuf)) {\n+\t\t\tif (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {\n \t\t\t\tfree(buf);\n \t\t\t\tsize = strbuf.len;\n \t\t\t\tbuf = strbuf_detach(&strbuf, NULL);\n \t\t\t}\n \t\t}\n \n-\t\tif (make_room_for_path(o, path) < 0) {\n+\t\tif (make_room_for_path(opt, path) < 0) {\n \t\t\tupdate_wd = 0;\n \t\t\tgoto free_buf;\n \t\t}\n@@ -1001,7 +1001,7 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\tret = err(opt, _(\"failed to open '%s': %s\"),\n \t\t\t\t\t  path, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n@@ -1012,11 +1012,11 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\tret = err(opt, _(\"failed to symlink '%s': %s\"),\n \t\t\t\t\t  path, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = err(o,\n+\t\t\tret = err(opt,\n \t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t\t  mode, oid_to_hex(oid), path);\n \tfree_buf:\n@@ -1024,19 +1024,19 @@ static int update_file_flags(struct merge_options *o,\n \t}\n update_index:\n \tif (!ret && update_cache)\n-\t\tif (add_cacheinfo(o, mode, oid, path, 0, update_wd,\n+\t\tif (add_cacheinfo(opt, mode, oid, path, 0, update_wd,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD))\n \t\t\treturn -1;\n \treturn ret;\n }\n \n-static int update_file(struct merge_options *o,\n+static int update_file(struct merge_options *opt,\n \t\t       int clean,\n \t\t       const struct object_id *oid,\n \t\t       unsigned mode,\n \t\t       const char *path)\n {\n-\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(opt, oid, mode, path, opt->call_depth || clean, !opt->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1048,7 +1048,7 @@ struct merge_file_info {\n \t\t merge:1;\n };\n \n-static int merge_3way(struct merge_options *o,\n+static int merge_3way(struct merge_options *opt,\n \t\t      mmbuffer_t *result_buf,\n \t\t      const struct diff_filespec *one,\n \t\t      const struct diff_filespec *a,\n@@ -1062,15 +1062,15 @@ static int merge_3way(struct merge_options *o,\n \tchar *base_name, *name1, *name2;\n \tint merge_status;\n \n-\tll_opts.renormalize = o->renormalize;\n+\tll_opts.renormalize = opt->renormalize;\n \tll_opts.extra_marker_size = extra_marker_size;\n-\tll_opts.xdl_opts = o->xdl_opts;\n+\tll_opts.xdl_opts = opt->xdl_opts;\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\tll_opts.virtual_ancestor = 1;\n \t\tll_opts.variant = 0;\n \t} else {\n-\t\tswitch (o->recursive_variant) {\n+\t\tswitch (opt->recursive_variant) {\n \t\tcase MERGE_RECURSIVE_OURS:\n \t\t\tll_opts.variant = XDL_MERGE_FAVOR_OURS;\n \t\t\tbreak;\n@@ -1084,14 +1084,14 @@ static int merge_3way(struct merge_options *o,\n \t}\n \n \tif (strcmp(a->path, b->path) ||\n-\t    (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n-\t\tbase_name = o->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s:%s\", o->ancestor, one->path);\n+\t    (opt->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n+\t\tbase_name = opt->ancestor == NULL ? NULL :\n+\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, one->path);\n \t\tname1 = mkpathdup(\"%s:%s\", branch1, a->path);\n \t\tname2 = mkpathdup(\"%s:%s\", branch2, b->path);\n \t} else {\n-\t\tbase_name = o->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s\", o->ancestor);\n+\t\tbase_name = opt->ancestor == NULL ? NULL :\n+\t\t\tmkpathdup(\"%s\", opt->ancestor);\n \t\tname1 = mkpathdup(\"%s\", branch1);\n \t\tname2 = mkpathdup(\"%s\", branch2);\n \t}\n@@ -1102,7 +1102,7 @@ static int merge_3way(struct merge_options *o,\n \n \tmerge_status = ll_merge(result_buf, a->path, &orig, base_name,\n \t\t\t\t&src1, name1, &src2, name2,\n-\t\t\t\to->repo->index, &ll_opts);\n+\t\t\t\topt->repo->index, &ll_opts);\n \n \tfree(base_name);\n \tfree(name1);\n@@ -1184,7 +1184,7 @@ static void print_commit(struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n-static int merge_submodule(struct merge_options *o,\n+static int merge_submodule(struct merge_options *opt,\n \t\t\t   struct object_id *result, const char *path,\n \t\t\t   const struct object_id *base, const struct object_id *a,\n \t\t\t   const struct object_id *b)\n@@ -1194,7 +1194,7 @@ static int merge_submodule(struct merge_options *o,\n \tstruct object_array merges;\n \n \tint i;\n-\tint search = !o->call_depth;\n+\tint search = !opt->call_depth;\n \n \t/* store a in result in case we fail */\n \toidcpy(result, a);\n@@ -1208,32 +1208,32 @@ static int merge_submodule(struct merge_options *o,\n \t\treturn 0;\n \n \tif (add_submodule_odb(path)) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (not checked out)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (not checked out)\"), path);\n \t\treturn 0;\n \t}\n \n-\tif (!(commit_base = lookup_commit_reference(o->repo, base)) ||\n-\t    !(commit_a = lookup_commit_reference(o->repo, a)) ||\n-\t    !(commit_b = lookup_commit_reference(o->repo, b))) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (commits not present)\"), path);\n+\tif (!(commit_base = lookup_commit_reference(opt->repo, base)) ||\n+\t    !(commit_a = lookup_commit_reference(opt->repo, a)) ||\n+\t    !(commit_b = lookup_commit_reference(opt->repo, b))) {\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (commits not present)\"), path);\n \t\treturn 0;\n \t}\n \n \t/* check whether both changes are forward */\n \tif (!in_merge_bases(commit_base, commit_a) ||\n \t    !in_merge_bases(commit_base, commit_b)) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (commits don't follow merge-base)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (commits don't follow merge-base)\"), path);\n \t\treturn 0;\n \t}\n \n \t/* Case #1: a is contained in b or vice versa */\n \tif (in_merge_bases(commit_a, commit_b)) {\n \t\toidcpy(result, b);\n-\t\tif (show(o, 3)) {\n-\t\t\toutput(o, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n-\t\t\toutput_commit_title(o, commit_b);\n-\t\t} else if (show(o, 2))\n-\t\t\toutput(o, 2, _(\"Fast-forwarding submodule %s\"), path);\n+\t\tif (show(opt, 3)) {\n+\t\t\toutput(opt, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n+\t\t\toutput_commit_title(opt, commit_b);\n+\t\t} else if (show(opt, 2))\n+\t\t\toutput(opt, 2, _(\"Fast-forwarding submodule %s\"), path);\n \t\telse\n \t\t\t; /* no output */\n \n@@ -1241,11 +1241,11 @@ static int merge_submodule(struct merge_options *o,\n \t}\n \tif (in_merge_bases(commit_b, commit_a)) {\n \t\toidcpy(result, a);\n-\t\tif (show(o, 3)) {\n-\t\t\toutput(o, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n-\t\t\toutput_commit_title(o, commit_a);\n-\t\t} else if (show(o, 2))\n-\t\t\toutput(o, 2, _(\"Fast-forwarding submodule %s\"), path);\n+\t\tif (show(opt, 3)) {\n+\t\t\toutput(opt, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n+\t\t\toutput_commit_title(opt, commit_a);\n+\t\t} else if (show(opt, 2))\n+\t\t\toutput(opt, 2, _(\"Fast-forwarding submodule %s\"), path);\n \t\telse\n \t\t\t; /* no output */\n \n@@ -1264,18 +1264,18 @@ static int merge_submodule(struct merge_options *o,\n \t\treturn 0;\n \n \t/* find commit which merges them */\n-\tparent_count = find_first_merges(o->repo, &merges, path,\n+\tparent_count = find_first_merges(opt->repo, &merges, path,\n \t\t\t\t\t commit_a, commit_b);\n \tswitch (parent_count) {\n \tcase 0:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (merge following commits not found)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (merge following commits not found)\"), path);\n \t\tbreak;\n \n \tcase 1:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (not fast-forward)\"), path);\n-\t\toutput(o, 2, _(\"Found a possible merge resolution for the submodule:\\n\"));\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (not fast-forward)\"), path);\n+\t\toutput(opt, 2, _(\"Found a possible merge resolution for the submodule:\\n\"));\n \t\tprint_commit((struct commit *) merges.objects[0].item);\n-\t\toutput(o, 2, _(\n+\t\toutput(opt, 2, _(\n \t\t       \"If this is correct simply add it to the index \"\n \t\t       \"for example\\n\"\n \t\t       \"by using:\\n\\n\"\n@@ -1285,7 +1285,7 @@ static int merge_submodule(struct merge_options *o,\n \t\tbreak;\n \n \tdefault:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (multiple merges found)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (multiple merges found)\"), path);\n \t\tfor (i = 0; i < merges.nr; i++)\n \t\t\tprint_commit((struct commit *) merges.objects[i].item);\n \t}\n@@ -1294,7 +1294,7 @@ static int merge_submodule(struct merge_options *o,\n \treturn 0;\n }\n \n-static int merge_mode_and_contents(struct merge_options *o,\n+static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t   const struct diff_filespec *b,\n@@ -1304,13 +1304,13 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\t\t   const int extra_marker_size,\n \t\t\t\t   struct merge_file_info *result)\n {\n-\tif (o->branch1 != branch1) {\n+\tif (opt->branch1 != branch1) {\n \t\t/*\n \t\t * It's weird getting a reverse merge with HEAD on the bottom\n \t\t * side of the conflict markers and the other branch on the\n \t\t * top.  Fix that.\n \t\t */\n-\t\treturn merge_mode_and_contents(o, one, b, a,\n+\t\treturn merge_mode_and_contents(opt, one, b, a,\n \t\t\t\t\t       filename,\n \t\t\t\t\t       branch2, branch1,\n \t\t\t\t\t       extra_marker_size, result);\n@@ -1353,17 +1353,17 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n \n-\t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n+\t\t\tmerge_status = merge_3way(opt, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2,\n \t\t\t\t\t\t  extra_marker_size);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(opt, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret &&\n \t\t\t    write_object_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t      blob_type, &result->oid))\n-\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n+\t\t\t\tret = err(opt, _(\"Unable to add %s to database\"),\n \t\t\t\t\t  a->path);\n \n \t\t\tfree(result_buf.ptr);\n@@ -1371,13 +1371,13 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult->clean = merge_submodule(o, &result->oid,\n+\t\t\tresult->clean = merge_submodule(opt, &result->oid,\n \t\t\t\t\t\t\tone->path,\n \t\t\t\t\t\t\t&one->oid,\n \t\t\t\t\t\t\t&a->oid,\n \t\t\t\t\t\t\t&b->oid);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\tswitch (o->recursive_variant) {\n+\t\t\tswitch (opt->recursive_variant) {\n \t\t\tcase MERGE_RECURSIVE_NORMAL:\n \t\t\t\toidcpy(&result->oid, &a->oid);\n \t\t\t\tif (!oid_eq(&a->oid, &b->oid))\n@@ -1395,12 +1395,12 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t}\n \n \tif (result->merge)\n-\t\toutput(o, 2, _(\"Auto-merging %s\"), filename);\n+\t\toutput(opt, 2, _(\"Auto-merging %s\"), filename);\n \n \treturn 0;\n }\n \n-static int handle_rename_via_dir(struct merge_options *o,\n+static int handle_rename_via_dir(struct merge_options *opt,\n \t\t\t\t struct diff_filepair *pair,\n \t\t\t\t const char *rename_branch)\n {\n@@ -1412,10 +1412,10 @@ static int handle_rename_via_dir(struct merge_options *o,\n \t */\n \tconst struct diff_filespec *dest = pair->two;\n \n-\tif (!o->call_depth && would_lose_untracked(o, dest->path)) {\n-\t\tchar *alt_path = unique_path(o, dest->path, rename_branch);\n+\tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n+\t\tchar *alt_path = unique_path(opt, dest->path, rename_branch);\n \n-\t\toutput(o, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n+\t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n \t\t\t       \"writing to %s instead.\"),\n \t\t       dest->path, alt_path);\n \t\t/*\n@@ -1423,21 +1423,21 @@ static int handle_rename_via_dir(struct merge_options *o,\n \t\t * index.  Instead, write to dest->path for the index but\n \t\t * only at the higher appropriate stage.\n \t\t */\n-\t\tif (update_file(o, 0, &dest->oid, dest->mode, alt_path))\n+\t\tif (update_file(opt, 0, &dest->oid, dest->mode, alt_path))\n \t\t\treturn -1;\n \t\tfree(alt_path);\n-\t\treturn update_stages(o, dest->path, NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n+\t\treturn update_stages(opt, dest->path, NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n-\tif (update_file(o, 1, &dest->oid, dest->mode, dest->path))\n+\tif (update_file(opt, 1, &dest->oid, dest->mode, dest->path))\n \t\treturn -1;\n \treturn 0;\n }\n \n-static int handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *opt,\n \t\t\t\tconst char *path, const char *old_path,\n \t\t\t\tconst struct object_id *o_oid, int o_mode,\n \t\t\t\tconst struct object_id *changed_oid,\n@@ -1450,20 +1450,20 @@ static int handle_change_delete(struct merge_options *o,\n \tconst char *update_path = path;\n \tint ret = 0;\n \n-\tif (dir_in_way(o->repo->index, path, !o->call_depth, 0) ||\n-\t    (!o->call_depth && would_lose_untracked(o, path))) {\n-\t\tupdate_path = alt_path = unique_path(o, path, change_branch);\n+\tif (dir_in_way(opt->repo->index, path, !opt->call_depth, 0) ||\n+\t    (!opt->call_depth && would_lose_untracked(opt, path))) {\n+\t\tupdate_path = alt_path = unique_path(opt, path, change_branch);\n \t}\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\t/*\n \t\t * We cannot arbitrarily accept either a_sha or b_sha as\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tret = remove_file_from_index(o->repo->index, path);\n+\t\tret = remove_file_from_index(opt->repo->index, path);\n \t\tif (!ret)\n-\t\t\tret = update_file(o, 0, o_oid, o_mode, update_path);\n+\t\t\tret = update_file(opt, 0, o_oid, o_mode, update_path);\n \t} else {\n \t\t/*\n \t\t * Despite the four nearly duplicate messages and argument\n@@ -1482,24 +1482,24 @@ static int handle_change_delete(struct merge_options *o,\n \t\t */\n \t\tif (!alt_path) {\n \t\t\tif (!old_path) {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t\t       change, path, delete_branch, change_past,\n \t\t\t\t       change_branch, change_branch, path);\n \t\t\t} else {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s to %s in %s. Version %s of %s left in tree.\"),\n \t\t\t\t       change, old_path, delete_branch, change_past, path,\n \t\t\t\t       change_branch, change_branch, path);\n \t\t\t}\n \t\t} else {\n \t\t\tif (!old_path) {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t\t       change, path, delete_branch, change_past,\n \t\t\t\t       change_branch, change_branch, path, alt_path);\n \t\t\t} else {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s to %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t\t       change, old_path, delete_branch, change_past, path,\n \t\t\t\t       change_branch, change_branch, path, alt_path);\n@@ -1507,19 +1507,19 @@ static int handle_change_delete(struct merge_options *o,\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when change_branch ==\n-\t\t * o->branch1 && !alt_path, since that would needlessly touch\n+\t\t * opt->branch1 && !alt_path, since that would needlessly touch\n \t\t * path.  We could call update_file_flags() with update_cache=0\n \t\t * and update_wd=0, but that's a no-op.\n \t\t */\n-\t\tif (change_branch != o->branch1 || alt_path)\n-\t\t\tret = update_file(o, 0, changed_oid, changed_mode, update_path);\n+\t\tif (change_branch != opt->branch1 || alt_path)\n+\t\t\tret = update_file(opt, 0, changed_oid, changed_mode, update_path);\n \t}\n \tfree(alt_path);\n \n \treturn ret;\n }\n \n-static int handle_rename_delete(struct merge_options *o,\n+static int handle_rename_delete(struct merge_options *opt,\n \t\t\t\tstruct diff_filepair *pair,\n \t\t\t\tconst char *rename_branch,\n \t\t\t\tconst char *delete_branch)\n@@ -1527,21 +1527,21 @@ static int handle_rename_delete(struct merge_options *o,\n \tconst struct diff_filespec *orig = pair->one;\n \tconst struct diff_filespec *dest = pair->two;\n \n-\tif (handle_change_delete(o,\n-\t\t\t\t o->call_depth ? orig->path : dest->path,\n-\t\t\t\t o->call_depth ? NULL : orig->path,\n+\tif (handle_change_delete(opt,\n+\t\t\t\t opt->call_depth ? orig->path : dest->path,\n+\t\t\t\t opt->call_depth ? NULL : orig->path,\n \t\t\t\t &orig->oid, orig->mode,\n \t\t\t\t &dest->oid, dest->mode,\n \t\t\t\t rename_branch, delete_branch,\n \t\t\t\t _(\"rename\"), _(\"renamed\")))\n \t\treturn -1;\n \n-\tif (o->call_depth)\n-\t\treturn remove_file_from_index(o->repo->index, dest->path);\n+\tif (opt->call_depth)\n+\t\treturn remove_file_from_index(opt->repo->index, dest->path);\n \telse\n-\t\treturn update_stages(o, dest->path, NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n+\t\treturn update_stages(opt, dest->path, NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1557,7 +1557,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static int handle_file_collision(struct merge_options *o,\n+static int handle_file_collision(struct merge_options *opt,\n \t\t\t\t const char *collide_path,\n \t\t\t\t const char *prev_path1,\n \t\t\t\t const char *prev_path2,\n@@ -1575,11 +1575,11 @@ static int handle_file_collision(struct merge_options *o,\n \t/*\n \t * It's easiest to get the correct things into stage 2 and 3, and\n \t * to make sure that the content merge puts HEAD before the other\n-\t * branch if we just ensure that branch1 == o->branch1.  So, simply\n+\t * branch if we just ensure that branch1 == opt->branch1.  So, simply\n \t * flip arguments around if we don't have that.\n \t */\n-\tif (branch1 != o->branch1) {\n-\t\treturn handle_file_collision(o, collide_path,\n+\tif (branch1 != opt->branch1) {\n+\t\treturn handle_file_collision(opt, collide_path,\n \t\t\t\t\t     prev_path2, prev_path1,\n \t\t\t\t\t     branch2, branch1,\n \t\t\t\t\t     b_oid, b_mode,\n@@ -1589,60 +1589,60 @@ static int handle_file_collision(struct merge_options *o,\n \t/*\n \t * In the recursive case, we just opt to undo renames\n \t */\n-\tif (o->call_depth && (prev_path1 || prev_path2)) {\n+\tif (opt->call_depth && (prev_path1 || prev_path2)) {\n \t\t/* Put first file (a_oid, a_mode) in its original spot */\n \t\tif (prev_path1) {\n-\t\t\tif (update_file(o, 1, a_oid, a_mode, prev_path1))\n+\t\t\tif (update_file(opt, 1, a_oid, a_mode, prev_path1))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(o, 1, a_oid, a_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, a_oid, a_mode, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\t/* Put second file (b_oid, b_mode) in its original spot */\n \t\tif (prev_path2) {\n-\t\t\tif (update_file(o, 1, b_oid, b_mode, prev_path2))\n+\t\t\tif (update_file(opt, 1, b_oid, b_mode, prev_path2))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(o, 1, b_oid, b_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, b_oid, b_mode, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\t/* Don't leave something at collision path if unrenaming both */\n \t\tif (prev_path1 && prev_path2)\n-\t\t\tremove_file(o, 1, collide_path, 0);\n+\t\t\tremove_file(opt, 1, collide_path, 0);\n \n \t\treturn 0;\n \t}\n \n \t/* Remove rename sources if rename/add or rename/rename(2to1) */\n \tif (prev_path1)\n-\t\tremove_file(o, 1, prev_path1,\n-\t\t\t    o->call_depth || would_lose_untracked(o, prev_path1));\n+\t\tremove_file(opt, 1, prev_path1,\n+\t\t\t    opt->call_depth || would_lose_untracked(opt, prev_path1));\n \tif (prev_path2)\n-\t\tremove_file(o, 1, prev_path2,\n-\t\t\t    o->call_depth || would_lose_untracked(o, prev_path2));\n+\t\tremove_file(opt, 1, prev_path2,\n+\t\t\t    opt->call_depth || would_lose_untracked(opt, prev_path2));\n \n \t/*\n \t * Remove the collision path, if it wouldn't cause dirty contents\n \t * or an untracked file to get lost.  We'll either overwrite with\n \t * merged contents, or just write out to differently named files.\n \t */\n-\tif (was_dirty(o, collide_path)) {\n-\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\tif (was_dirty(opt, collide_path)) {\n+\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t       collide_path);\n-\t\tupdate_path = alt_path = unique_path(o, collide_path, \"merged\");\n-\t} else if (would_lose_untracked(o, collide_path)) {\n+\t\tupdate_path = alt_path = unique_path(opt, collide_path, \"merged\");\n+\t} else if (would_lose_untracked(opt, collide_path)) {\n \t\t/*\n \t\t * Only way we get here is if both renames were from\n \t\t * a directory rename AND user had an untracked file\n \t\t * at the location where both files end up after the\n \t\t * two directory renames.  See testcase 10d of t6043.\n \t\t */\n-\t\toutput(o, 1, _(\"Refusing to lose untracked file at \"\n+\t\toutput(opt, 1, _(\"Refusing to lose untracked file at \"\n \t\t\t       \"%s, even though it's in the way.\"),\n \t\t       collide_path);\n-\t\tupdate_path = alt_path = unique_path(o, collide_path, \"merged\");\n+\t\tupdate_path = alt_path = unique_path(opt, collide_path, \"merged\");\n \t} else {\n \t\t/*\n \t\t * FIXME: It's possible that the two files are identical\n@@ -1654,7 +1654,7 @@ static int handle_file_collision(struct merge_options *o,\n \t\t * merge-recursive interoperate anyway, so punting for\n \t\t * now...\n \t\t */\n-\t\tremove_file(o, 0, collide_path, 0);\n+\t\tremove_file(opt, 0, collide_path, 0);\n \t}\n \n \t/* Store things in diff_filespecs for functions that need it */\n@@ -1670,14 +1670,14 @@ static int handle_file_collision(struct merge_options *o,\n \tb.mode = b_mode;\n \tb.oid_valid = 1;\n \n-\tif (merge_mode_and_contents(o, &null, &a, &b, collide_path,\n-\t\t\t\t    branch1, branch2, o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, &null, &a, &b, collide_path,\n+\t\t\t\t    branch1, branch2, opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tmfi.clean &= !alt_path;\n-\tif (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path))\n+\tif (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, update_path))\n \t\treturn -1;\n-\tif (!mfi.clean && !o->call_depth &&\n-\t    update_stages(o, collide_path, NULL, &a, &b))\n+\tif (!mfi.clean && !opt->call_depth &&\n+\t    update_stages(opt, collide_path, NULL, &a, &b))\n \t\treturn -1;\n \tfree(alt_path);\n \t/*\n@@ -1690,7 +1690,7 @@ static int handle_file_collision(struct merge_options *o,\n \treturn mfi.clean;\n }\n \n-static int handle_rename_add(struct merge_options *o,\n+static int handle_rename_add(struct merge_options *opt,\n \t\t\t     struct rename_conflict_info *ci)\n {\n \t/* a was renamed to c, and a separate c was added. */\n@@ -1700,21 +1700,21 @@ static int handle_rename_add(struct merge_options *o,\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n \n-\tint other_stage = (ci->branch1 == o->branch1 ? 3 : 2);\n+\tint other_stage = (ci->branch1 == opt->branch1 ? 3 : 2);\n \n-\toutput(o, 1, _(\"CONFLICT (rename/add): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/add): \"\n \t       \"Rename %s->%s in %s.  Added %s in %s\"),\n \t       a->path, c->path, ci->branch1,\n \t       c->path, ci->branch2);\n \n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, a, c, &ci->ren1_other, prev_path_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(prev_path_desc);\n \n-\treturn handle_file_collision(o,\n+\treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi.oid, mfi.mode,\n@@ -1722,20 +1722,20 @@ static int handle_rename_add(struct merge_options *o,\n \t\t\t\t     ci->dst_entry1->stages[other_stage].mode);\n }\n \n-static char *find_path_for_conflict(struct merge_options *o,\n+static char *find_path_for_conflict(struct merge_options *opt,\n \t\t\t\t    const char *path,\n \t\t\t\t    const char *branch1,\n \t\t\t\t    const char *branch2)\n {\n \tchar *new_path = NULL;\n-\tif (dir_in_way(o->repo->index, path, !o->call_depth, 0)) {\n-\t\tnew_path = unique_path(o, path, branch1);\n-\t\toutput(o, 1, _(\"%s is a directory in %s adding \"\n+\tif (dir_in_way(opt->repo->index, path, !opt->call_depth, 0)) {\n+\t\tnew_path = unique_path(opt, path, branch1);\n+\t\toutput(opt, 1, _(\"%s is a directory in %s adding \"\n \t\t\t       \"as %s instead\"),\n \t\t       path, branch2, new_path);\n-\t} else if (would_lose_untracked(o, path)) {\n-\t\tnew_path = unique_path(o, path, branch1);\n-\t\toutput(o, 1, _(\"Refusing to lose untracked file\"\n+\t} else if (would_lose_untracked(opt, path)) {\n+\t\tnew_path = unique_path(opt, path, branch1);\n+\t\toutput(opt, 1, _(\"Refusing to lose untracked file\"\n \t\t\t       \" at %s; adding as %s instead\"),\n \t\t       path, new_path);\n \t}\n@@ -1743,7 +1743,7 @@ static char *find_path_for_conflict(struct merge_options *o,\n \treturn new_path;\n }\n \n-static int handle_rename_rename_1to2(struct merge_options *o,\n+static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1755,29 +1755,29 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \tstruct diff_filespec *b = ci->pair2->two;\n \tchar *path_desc;\n \n-\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n \t       one->path, a->path, ci->branch1,\n \t       one->path, b->path, ci->branch2,\n-\t       o->call_depth ? _(\" (left unresolved)\") : \"\");\n+\t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n \t\t\t    a->path, b->path, one->path);\n-\tif (merge_mode_and_contents(o, one, a, b, path_desc,\n+\tif (merge_mode_and_contents(opt, one, a, b, path_desc,\n \t\t\t\t    ci->branch1, ci->branch2,\n-\t\t\t\t    o->call_depth * 2, &mfi))\n+\t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(path_desc);\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, one->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -1790,18 +1790,18 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n \t\tif (add) {\n-\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n-\t\t\tremove_file_from_index(o->repo->index, a->path);\n+\t\t\tremove_file_from_index(opt->repo->index, a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n \t\tif (add) {\n-\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n-\t\t\tremove_file_from_index(o->repo->index, b->path);\n+\t\t\tremove_file_from_index(opt->repo->index, b->path);\n \t} else {\n \t\t/*\n \t\t * For each destination path, we need to see if there is a\n@@ -1810,39 +1810,39 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n \t\tif (add) {\n-\t\t\tif (handle_file_collision(o, a->path,\n+\t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->branch1, ci->branch2,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode,\n \t\t\t\t\t\t  &add->oid, add->mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tchar *new_path = find_path_for_conflict(o, a->path,\n+\t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n \t\t\t\t\t\t\t\tci->branch1,\n \t\t\t\t\t\t\t\tci->branch2);\n-\t\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n+\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n-\t\t\tif (update_stages(o, a->path, NULL, a, NULL))\n+\t\t\tif (update_stages(opt, a->path, NULL, a, NULL))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n \t\tif (add) {\n-\t\t\tif (handle_file_collision(o, b->path,\n+\t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->branch1, ci->branch2,\n \t\t\t\t\t\t  &add->oid, add->mode,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tchar *new_path = find_path_for_conflict(o, b->path,\n+\t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n \t\t\t\t\t\t\t\tci->branch2,\n \t\t\t\t\t\t\t\tci->branch1);\n-\t\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n+\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n-\t\t\tif (update_stages(o, b->path, NULL, NULL, b))\n+\t\t\tif (update_stages(opt, b->path, NULL, NULL, b))\n \t\t\t\treturn -1;\n \t\t}\n \t}\n@@ -1850,7 +1850,7 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \treturn 0;\n }\n \n-static int handle_rename_rename_2to1(struct merge_options *o,\n+static int handle_rename_rename_2to1(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1864,7 +1864,7 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n \n-\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n \t       \"Rename %s->%s in %s\"),\n \t       a->path, c1->path, ci->branch1,\n@@ -1872,17 +1872,17 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n \n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi_c2))\n+\tif (merge_mode_and_contents(opt, a, c1, &ci->ren1_other, path_side_1_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n+\t    merge_mode_and_contents(opt, b, &ci->ren2_other, c2, path_side_2_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n \tfree(path_side_1_desc);\n \tfree(path_side_2_desc);\n \n-\treturn handle_file_collision(o, path, a->path, b->path,\n+\treturn handle_file_collision(opt, path, a->path, b->path,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n \t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n@@ -1891,17 +1891,17 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n /*\n  * Get the diff_filepairs changed between o_tree and tree.\n  */\n-static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n+static struct diff_queue_struct *get_diffpairs(struct merge_options *opt,\n \t\t\t\t\t       struct tree *o_tree,\n \t\t\t\t\t       struct tree *tree)\n {\n \tstruct diff_queue_struct *ret;\n \tstruct diff_options opts;\n \n-\trepo_diff_setup(o->repo, &opts);\n+\trepo_diff_setup(opt->repo, &opts);\n \topts.flags.recursive = 1;\n \topts.flags.rename_empty = 0;\n-\topts.detect_rename = merge_detect_rename(o);\n+\topts.detect_rename = merge_detect_rename(opt);\n \t/*\n \t * We do not have logic to handle the detection of copies.  In\n \t * fact, it may not even make sense to add such logic: would we\n@@ -1910,17 +1910,17 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n \t */\n \tif (opts.detect_rename > DIFF_DETECT_RENAME)\n \t\topts.detect_rename = DIFF_DETECT_RENAME;\n-\topts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :\n-\t\t\t    o->diff_rename_limit >= 0 ? o->diff_rename_limit :\n+\topts.rename_limit = opt->merge_rename_limit >= 0 ? opt->merge_rename_limit :\n+\t\t\t    opt->diff_rename_limit >= 0 ? opt->diff_rename_limit :\n \t\t\t    1000;\n-\topts.rename_score = o->rename_score;\n-\topts.show_rename_progress = o->show_rename_progress;\n+\topts.rename_score = opt->rename_score;\n+\topts.show_rename_progress = opt->show_rename_progress;\n \topts.output_format = DIFF_FORMAT_NO_OUTPUT;\n \tdiff_setup_done(&opts);\n \tdiff_tree_oid(&o_tree->object.oid, &tree->object.oid, \"\", &opts);\n \tdiffcore_std(&opts);\n-\tif (opts.needed_rename_limit > o->needed_rename_limit)\n-\t\to->needed_rename_limit = opts.needed_rename_limit;\n+\tif (opts.needed_rename_limit > opt->needed_rename_limit)\n+\t\topt->needed_rename_limit = opts.needed_rename_limit;\n \n \tret = xmalloc(sizeof(*ret));\n \t*ret = diff_queued_diff;\n@@ -2039,7 +2039,7 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,\n  * level conflicts for the renamed location.  If there is a rename and\n  * there are no conflicts, return the new name.  Otherwise, return NULL.\n  */\n-static char *handle_path_level_conflicts(struct merge_options *o,\n+static char *handle_path_level_conflicts(struct merge_options *opt,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t struct dir_rename_entry *entry,\n \t\t\t\t\t struct hashmap *collisions,\n@@ -2060,7 +2060,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\t/* This should only happen when entry->non_unique_new_dir set */\n \t\tif (!entry->non_unique_new_dir)\n \t\t\tBUG(\"entry->non_unqiue_dir not set and !new_path\");\n-\t\toutput(o, 1, _(\"CONFLICT (directory rename split): \"\n+\t\toutput(opt, 1, _(\"CONFLICT (directory rename split): \"\n \t\t\t       \"Unclear where to place %s because directory \"\n \t\t\t       \"%s was renamed to multiple other directories, \"\n \t\t\t       \"with no destination getting a majority of the \"\n@@ -2092,7 +2092,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\tcollision_ent->reported_already = 1;\n \t\tstrbuf_add_separated_string_list(&collision_paths, \", \",\n \t\t\t\t\t\t &collision_ent->source_files);\n-\t\toutput(o, 1, _(\"CONFLICT (implicit dir rename): Existing \"\n+\t\toutput(opt, 1, _(\"CONFLICT (implicit dir rename): Existing \"\n \t\t\t       \"file/dir at %s in the way of implicit \"\n \t\t\t       \"directory rename(s) putting the following \"\n \t\t\t       \"path(s) there: %s.\"),\n@@ -2102,7 +2102,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\tcollision_ent->reported_already = 1;\n \t\tstrbuf_add_separated_string_list(&collision_paths, \", \",\n \t\t\t\t\t\t &collision_ent->source_files);\n-\t\toutput(o, 1, _(\"CONFLICT (implicit dir rename): Cannot map \"\n+\t\toutput(opt, 1, _(\"CONFLICT (implicit dir rename): Cannot map \"\n \t\t\t       \"more than one path to %s; implicit directory \"\n \t\t\t       \"renames tried to put these paths there: %s\"),\n \t\t       new_path, collision_paths.buf);\n@@ -2139,7 +2139,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n  *         causes conflicts for files within those merged directories, then\n  *         that should be detected at the individual path level.\n  */\n-static void handle_directory_level_conflicts(struct merge_options *o,\n+static void handle_directory_level_conflicts(struct merge_options *opt,\n \t\t\t\t\t     struct hashmap *dir_re_head,\n \t\t\t\t\t     struct tree *head,\n \t\t\t\t\t     struct hashmap *dir_re_merge,\n@@ -2194,11 +2194,11 @@ static void handle_directory_level_conflicts(struct merge_options *o,\n \t\t\t * know that head_ent->new_dir and merge_ent->new_dir\n \t\t\t * are different strings.\n \t\t\t */\n-\t\t\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\t\t\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t\t\t\t       \"Rename directory %s->%s in %s. \"\n \t\t\t\t       \"Rename directory %s->%s in %s\"),\n-\t\t\t       head_ent->dir, head_ent->new_dir.buf, o->branch1,\n-\t\t\t       head_ent->dir, merge_ent->new_dir.buf, o->branch2);\n+\t\t\t       head_ent->dir, head_ent->new_dir.buf, opt->branch1,\n+\t\t\t       head_ent->dir, merge_ent->new_dir.buf, opt->branch2);\n \t\t\tstring_list_append(&remove_from_head,\n \t\t\t\t\t   head_ent->dir)->util = head_ent;\n \t\t\tstrbuf_release(&head_ent->new_dir);\n@@ -2397,7 +2397,7 @@ static void compute_collisions(struct hashmap *collisions,\n \t}\n }\n \n-static char *check_for_directory_rename(struct merge_options *o,\n+static char *check_for_directory_rename(struct merge_options *opt,\n \t\t\t\t\tconst char *path,\n \t\t\t\t\tstruct tree *tree,\n \t\t\t\t\tstruct hashmap *dir_renames,\n@@ -2438,11 +2438,11 @@ static char *check_for_directory_rename(struct merge_options *o,\n \t */\n \toentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);\n \tif (oentry) {\n-\t\toutput(o, 1, _(\"WARNING: Avoiding applying %s -> %s rename \"\n+\t\toutput(opt, 1, _(\"WARNING: Avoiding applying %s -> %s rename \"\n \t\t\t       \"to %s, because %s itself was renamed.\"),\n \t\t       entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);\n \t} else {\n-\t\tnew_path = handle_path_level_conflicts(o, path, entry,\n+\t\tnew_path = handle_path_level_conflicts(opt, path, entry,\n \t\t\t\t\t\t       collisions, tree);\n \t\t*clean_merge &= (new_path != NULL);\n \t}\n@@ -2450,7 +2450,7 @@ static char *check_for_directory_rename(struct merge_options *o,\n \treturn new_path;\n }\n \n-static void apply_directory_rename_modifications(struct merge_options *o,\n+static void apply_directory_rename_modifications(struct merge_options *opt,\n \t\t\t\t\t\t struct diff_filepair *pair,\n \t\t\t\t\t\t char *new_path,\n \t\t\t\t\t\t struct rename *re,\n@@ -2473,11 +2473,11 @@ static void apply_directory_rename_modifications(struct merge_options *o,\n \t * saying the file would have been overwritten), but it might\n \t * be dirty, though.\n \t */\n-\tupdate_wd = !was_dirty(o, pair->two->path);\n+\tupdate_wd = !was_dirty(opt, pair->two->path);\n \tif (!update_wd)\n-\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t       pair->two->path);\n-\tremove_file(o, 1, pair->two->path, !update_wd);\n+\tremove_file(opt, 1, pair->two->path, !update_wd);\n \n \t/* Find or create a new re->dst_entry */\n \titem = string_list_lookup(entries, new_path);\n@@ -2566,7 +2566,7 @@ static void apply_directory_rename_modifications(struct merge_options *o,\n  * to be able to associate the correct cache entries with the rename\n  * information; tree is always equal to either a_tree or b_tree.\n  */\n-static struct string_list *get_renames(struct merge_options *o,\n+static struct string_list *get_renames(struct merge_options *opt,\n \t\t\t\t       struct diff_queue_struct *pairs,\n \t\t\t\t       struct hashmap *dir_renames,\n \t\t\t\t       struct hashmap *dir_rename_exclusions,\n@@ -2596,7 +2596,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \t\t\tdiff_free_filepair(pair);\n \t\t\tcontinue;\n \t\t}\n-\t\tnew_path = check_for_directory_rename(o, pair->two->path, tree,\n+\t\tnew_path = check_for_directory_rename(opt, pair->two->path, tree,\n \t\t\t\t\t\t      dir_renames,\n \t\t\t\t\t\t      dir_rename_exclusions,\n \t\t\t\t\t\t      &collisions,\n@@ -2626,7 +2626,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \t\titem = string_list_insert(renames, pair->one->path);\n \t\titem->util = re;\n \t\tif (new_path)\n-\t\t\tapply_directory_rename_modifications(o, pair, new_path,\n+\t\t\tapply_directory_rename_modifications(opt, pair, new_path,\n \t\t\t\t\t\t\t     re, tree, o_tree,\n \t\t\t\t\t\t\t     a_tree, b_tree,\n \t\t\t\t\t\t\t     entries);\n@@ -2641,7 +2641,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int process_renames(struct merge_options *o,\n+static int process_renames(struct merge_options *opt,\n \t\t\t   struct string_list *a_renames,\n \t\t\t   struct string_list *b_renames)\n {\n@@ -2685,13 +2685,13 @@ static int process_renames(struct merge_options *o,\n \t\tif (ren1) {\n \t\t\trenames1 = a_renames;\n \t\t\trenames2Dst = &b_by_dst;\n-\t\t\tbranch1 = o->branch1;\n-\t\t\tbranch2 = o->branch2;\n+\t\t\tbranch1 = opt->branch1;\n+\t\t\tbranch2 = opt->branch2;\n \t\t} else {\n \t\t\trenames1 = b_renames;\n \t\t\trenames2Dst = &a_by_dst;\n-\t\t\tbranch1 = o->branch2;\n-\t\t\tbranch2 = o->branch1;\n+\t\t\tbranch1 = opt->branch2;\n+\t\t\tbranch2 = opt->branch1;\n \t\t\tSWAP(ren2, ren1);\n \t\t}\n \n@@ -2725,7 +2725,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * the base stage (think of rename +\n \t\t\t\t * add-source cases).\n \t\t\t\t */\n-\t\t\t\tremove_file(o, 1, ren1_src, 1);\n+\t\t\t\tremove_file(opt, 1, ren1_src, 1);\n \t\t\t\tupdate_entry(ren1->dst_entry,\n \t\t\t\t\t     ren1->pair->one,\n \t\t\t\t\t     ren1->pair->two,\n@@ -2738,7 +2738,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t   NULL);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n@@ -2765,7 +2765,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t   ren2->src_entry);\n \n@@ -2788,8 +2788,8 @@ static int process_renames(struct merge_options *o,\n \t\t\t * stage and in other_stage (think of rename +\n \t\t\t * add-source case).\n \t\t\t */\n-\t\t\tremove_file(o, 1, ren1_src,\n-\t\t\t\t    renamed_stage == 2 || !was_tracked(o, ren1_src));\n+\t\t\tremove_file(opt, 1, ren1_src,\n+\t\t\t\t    renamed_stage == 2 || !was_tracked(opt, ren1_src));\n \n \t\t\toidcpy(&src_other.oid,\n \t\t\t       &ren1->src_entry->stages[other_stage].oid);\n@@ -2808,7 +2808,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n@@ -2819,7 +2819,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n@@ -2832,7 +2832,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tif (update_file_flags(o,\n+\t\t\t\tif (update_file_flags(opt,\n \t\t\t\t\t\t      &ren1->pair->two->oid,\n \t\t\t\t\t\t      ren1->pair->two->mode,\n \t\t\t\t\t\t      ren1_dst,\n@@ -2854,7 +2854,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else\n@@ -2882,7 +2882,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n@@ -2919,7 +2919,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,\n \tfree(pairs);\n }\n \n-static int detect_and_process_renames(struct merge_options *o,\n+static int detect_and_process_renames(struct merge_options *opt,\n \t\t\t\t      struct tree *common,\n \t\t\t\t      struct tree *head,\n \t\t\t\t      struct tree *merge,\n@@ -2933,17 +2933,17 @@ static int detect_and_process_renames(struct merge_options *o,\n \tri->head_renames = NULL;\n \tri->merge_renames = NULL;\n \n-\tif (!merge_detect_rename(o))\n+\tif (!merge_detect_rename(opt))\n \t\treturn 1;\n \n-\thead_pairs = get_diffpairs(o, common, head);\n-\tmerge_pairs = get_diffpairs(o, common, merge);\n+\thead_pairs = get_diffpairs(opt, common, head);\n+\tmerge_pairs = get_diffpairs(opt, common, merge);\n \n-\tif (o->detect_directory_renames) {\n+\tif (opt->detect_directory_renames) {\n \t\tdir_re_head = get_directory_renames(head_pairs);\n \t\tdir_re_merge = get_directory_renames(merge_pairs);\n \n-\t\thandle_directory_level_conflicts(o,\n+\t\thandle_directory_level_conflicts(opt,\n \t\t\t\t\t\t dir_re_head, head,\n \t\t\t\t\t\t dir_re_merge, merge);\n \t} else {\n@@ -2953,19 +2953,19 @@ static int detect_and_process_renames(struct merge_options *o,\n \t\tdir_rename_init(dir_re_merge);\n \t}\n \n-\tri->head_renames  = get_renames(o, head_pairs,\n+\tri->head_renames  = get_renames(opt, head_pairs,\n \t\t\t\t\tdir_re_merge, dir_re_head, head,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tri->merge_renames = get_renames(o, merge_pairs,\n+\tri->merge_renames = get_renames(opt, merge_pairs,\n \t\t\t\t\tdir_re_head, dir_re_merge, merge,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tclean &= process_renames(o, ri->head_renames, ri->merge_renames);\n+\tclean &= process_renames(opt, ri->head_renames, ri->merge_renames);\n \n cleanup:\n \t/*\n@@ -3006,7 +3006,7 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(struct merge_options *o,\n+static int read_oid_strbuf(struct merge_options *opt,\n \t\t\t   const struct object_id *oid,\n \t\t\t   struct strbuf *dst)\n {\n@@ -3015,10 +3015,10 @@ static int read_oid_strbuf(struct merge_options *o,\n \tunsigned long size;\n \tbuf = read_object_file(oid, &type, &size);\n \tif (!buf)\n-\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(opt, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(opt, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n@@ -3060,7 +3060,7 @@ static int blob_unchanged(struct merge_options *opt,\n \treturn ret;\n }\n \n-static int handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n \t\t\t\tstruct object_id *a_oid, int a_mode,\n@@ -3071,18 +3071,18 @@ static int handle_modify_delete(struct merge_options *o,\n \tint changed_mode;\n \n \tif (a_oid) {\n-\t\tmodify_branch = o->branch1;\n-\t\tdelete_branch = o->branch2;\n+\t\tmodify_branch = opt->branch1;\n+\t\tdelete_branch = opt->branch2;\n \t\tchanged_oid = a_oid;\n \t\tchanged_mode = a_mode;\n \t} else {\n-\t\tmodify_branch = o->branch2;\n-\t\tdelete_branch = o->branch1;\n+\t\tmodify_branch = opt->branch2;\n+\t\tdelete_branch = opt->branch1;\n \t\tchanged_oid = b_oid;\n \t\tchanged_mode = b_mode;\n \t}\n \n-\treturn handle_change_delete(o,\n+\treturn handle_change_delete(opt,\n \t\t\t\t    path, NULL,\n \t\t\t\t    o_oid, o_mode,\n \t\t\t\t    changed_oid, changed_mode,\n@@ -3090,7 +3090,7 @@ static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n-static int handle_content_merge(struct merge_options *o,\n+static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n@@ -3119,26 +3119,26 @@ static int handle_content_merge(struct merge_options *o,\n \tif (rename_conflict_info) {\n \t\tstruct diff_filepair *pair1 = rename_conflict_info->pair1;\n \n-\t\tpath1 = (o->branch1 == rename_conflict_info->branch1) ?\n+\t\tpath1 = (opt->branch1 == rename_conflict_info->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\t/* If rename_conflict_info->pair2 != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n \t\tpath2 = (rename_conflict_info->pair2 ||\n-\t\t\t o->branch2 == rename_conflict_info->branch1) ?\n+\t\t\t opt->branch2 == rename_conflict_info->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n \t\tb.path = (char *)path2;\n \n-\t\tif (dir_in_way(o->repo->index, path, !o->call_depth,\n+\t\tif (dir_in_way(opt->repo->index, path, !opt->call_depth,\n \t\t\t       S_ISGITLINK(pair1->two->mode)))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tif (merge_mode_and_contents(o, &one, &a, &b, path,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, &one, &a, &b, path,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \n \t/*\n@@ -3148,14 +3148,14 @@ static int handle_content_merge(struct merge_options *o,\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n \tif (mfi.clean &&\n-\t    was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&\n+\t    was_tracked_and_matches(opt, path, &mfi.oid, mfi.mode) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n-\t\toutput(o, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(o, mfi.mode, &mfi.oid, path,\n-\t\t\t\t  0, (!o->call_depth && !is_dirty), 0))\n+\t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n+\t\tif (add_cacheinfo(opt, mfi.mode, &mfi.oid, path,\n+\t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n \t\t * However, add_cacheinfo() will delete the old cache entry\n@@ -3163,11 +3163,11 @@ static int handle_content_merge(struct merge_options *o,\n \t\t * flag to avoid making the file appear as if it were\n \t\t * deleted by the user.\n \t\t */\n-\t\tpos = index_name_pos(&o->orig_index, path, strlen(path));\n-\t\tce = o->orig_index.cache[pos];\n+\t\tpos = index_name_pos(&opt->orig_index, path, strlen(path));\n+\t\tce = opt->orig_index.cache[pos];\n \t\tif (ce_skip_worktree(ce)) {\n-\t\t\tpos = index_name_pos(o->repo->index, path, strlen(path));\n-\t\t\tce = o->repo->index->cache[pos];\n+\t\t\tpos = index_name_pos(opt->repo->index, path, strlen(path));\n+\t\t\tce = opt->repo->index->cache[pos];\n \t\t\tce->ce_flags |= CE_SKIP_WORKTREE;\n \t\t}\n \t\treturn mfi.clean;\n@@ -3176,52 +3176,52 @@ static int handle_content_merge(struct merge_options *o,\n \tif (!mfi.clean) {\n \t\tif (S_ISGITLINK(mfi.mode))\n \t\t\treason = _(\"submodule\");\n-\t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n+\t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(o, path, &one, &a, &b))\n+\t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains || is_dirty) {\n \t\tchar *new_path;\n-\t\tif (o->call_depth) {\n-\t\t\tremove_file_from_index(o->repo->index, path);\n+\t\tif (opt->call_depth) {\n+\t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n+\t\t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n-\t\t\t\tint file_from_stage2 = was_tracked(o, path);\n+\t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(o, path, NULL,\n+\t\t\t\tif (update_stages(opt, path, NULL,\n \t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n+\t\tnew_path = unique_path(opt, path, rename_conflict_info->branch1);\n \t\tif (is_dirty) {\n-\t\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n \t\t}\n-\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t} else if (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, path))\n \t\treturn -1;\n \treturn !is_dirty && mfi.clean;\n }\n \n-static int handle_rename_normal(struct merge_options *o,\n+static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tstruct object_id *o_oid, unsigned int o_mode,\n \t\t\t\tstruct object_id *a_oid, unsigned int a_mode,\n@@ -3229,17 +3229,17 @@ static int handle_rename_normal(struct merge_options *o,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n-\treturn handle_content_merge(o, path, was_dirty(o, path),\n+\treturn handle_content_merge(opt, path, was_dirty(opt, path),\n \t\t\t\t    o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,\n \t\t\t\t    ci);\n }\n \n /* Per entry merge function */\n-static int process_entry(struct merge_options *o,\n+static int process_entry(struct merge_options *opt,\n \t\t\t const char *path, struct stage_data *entry)\n {\n \tint clean_merge = 1;\n-\tint normalize = o->renormalize;\n+\tint normalize = opt->renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -3253,7 +3253,7 @@ static int process_entry(struct merge_options *o,\n \t\tswitch (conflict_info->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n-\t\t\tclean_merge = handle_rename_normal(o,\n+\t\t\tclean_merge = handle_rename_normal(opt,\n \t\t\t\t\t\t\t   path,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n@@ -3262,7 +3262,7 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(o,\n+\t\t\tif (handle_rename_via_dir(opt,\n \t\t\t\t\t\t  conflict_info->pair1,\n \t\t\t\t\t\t  conflict_info->branch1))\n \t\t\t\tclean_merge = -1;\n@@ -3274,11 +3274,11 @@ static int process_entry(struct merge_options *o,\n \t\t\t * two-way merged cleanly with the added file, I\n \t\t\t * guess it's a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_add(o, conflict_info);\n+\t\t\tclean_merge = handle_rename_add(opt, conflict_info);\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(o,\n+\t\t\tif (handle_rename_delete(opt,\n \t\t\t\t\t\t conflict_info->pair1,\n \t\t\t\t\t\t conflict_info->branch1,\n \t\t\t\t\t\t conflict_info->branch2))\n@@ -3286,7 +3286,7 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_rename_1to2(o, conflict_info))\n+\t\t\tif (handle_rename_rename_1to2(opt, conflict_info))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n@@ -3296,7 +3296,7 @@ static int process_entry(struct merge_options *o,\n \t\t\t * can then be two-way merged cleanly, I guess it's\n \t\t\t * a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_rename_2to1(o,\n+\t\t\tclean_merge = handle_rename_rename_2to1(opt,\n \t\t\t\t\t\t\t\tconflict_info);\n \t\t\tbreak;\n \t\tdefault:\n@@ -3306,18 +3306,18 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(opt, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(opt, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n-\t\t\t\toutput(o, 2, _(\"Removing %s\"), path);\n+\t\t\t\toutput(opt, 2, _(\"Removing %s\"), path);\n \t\t\t/* do not touch working file if it did not exist */\n-\t\t\tremove_file(o, 1, path, !a_oid);\n+\t\t\tremove_file(opt, 1, path, !a_oid);\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\tif (handle_modify_delete(opt, path, o_oid, o_mode,\n \t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n \t\t\t\tclean_merge = -1;\n \t\t}\n@@ -3333,53 +3333,53 @@ static int process_entry(struct merge_options *o,\n \t\tconst char *conf;\n \n \t\tif (a_oid) {\n-\t\t\tadd_branch = o->branch1;\n-\t\t\tother_branch = o->branch2;\n+\t\t\tadd_branch = opt->branch1;\n+\t\t\tother_branch = opt->branch2;\n \t\t\tmode = a_mode;\n \t\t\toid = a_oid;\n \t\t\tconf = _(\"file/directory\");\n \t\t} else {\n-\t\t\tadd_branch = o->branch2;\n-\t\t\tother_branch = o->branch1;\n+\t\t\tadd_branch = opt->branch2;\n+\t\t\tother_branch = opt->branch1;\n \t\t\tmode = b_mode;\n \t\t\toid = b_oid;\n \t\t\tconf = _(\"directory/file\");\n \t\t}\n-\t\tif (dir_in_way(o->repo->index, path,\n-\t\t\t       !o->call_depth && !S_ISGITLINK(a_mode),\n+\t\tif (dir_in_way(opt->repo->index, path,\n+\t\t\t       !opt->call_depth && !S_ISGITLINK(a_mode),\n \t\t\t       0)) {\n-\t\t\tchar *new_path = unique_path(o, path, add_branch);\n+\t\t\tchar *new_path = unique_path(opt, path, add_branch);\n \t\t\tclean_merge = 0;\n-\t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n+\t\t\toutput(opt, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\tif (update_file(opt, 0, oid, mode, new_path))\n \t\t\t\tclean_merge = -1;\n-\t\t\telse if (o->call_depth)\n-\t\t\t\tremove_file_from_index(o->repo->index, path);\n+\t\t\telse if (opt->call_depth)\n+\t\t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t\tfree(new_path);\n \t\t} else {\n-\t\t\toutput(o, 2, _(\"Adding %s\"), path);\n+\t\t\toutput(opt, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\tif (update_file_flags(opt, oid, mode, path, 1, !a_oid))\n \t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\tif (!o_oid) {\n \t\t\t/* Case C: Added in both (check for same permissions) */\n-\t\t\toutput(o, 1,\n+\t\t\toutput(opt, 1,\n \t\t\t       _(\"CONFLICT (add/add): Merge conflict in %s\"),\n \t\t\t       path);\n-\t\t\tclean_merge = handle_file_collision(o,\n+\t\t\tclean_merge = handle_file_collision(opt,\n \t\t\t\t\t\t\t    path, NULL, NULL,\n-\t\t\t\t\t\t\t    o->branch1,\n-\t\t\t\t\t\t\t    o->branch2,\n+\t\t\t\t\t\t\t    opt->branch1,\n+\t\t\t\t\t\t\t    opt->branch2,\n \t\t\t\t\t\t\t    a_oid, a_mode,\n \t\t\t\t\t\t\t    b_oid, b_mode);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(o, path,\n+\t\t\tclean_merge = handle_content_merge(opt, path,\n \t\t\t\t\t\t\t   is_dirty,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n@@ -3391,48 +3391,48 @@ static int process_entry(struct merge_options *o,\n \t\t * this entry was deleted altogether. a_mode == 0 means\n \t\t * we had that path and want to actively remove it.\n \t\t */\n-\t\tremove_file(o, 1, path, !a_mode);\n+\t\tremove_file(opt, 1, path, !a_mode);\n \t} else\n \t\tBUG(\"fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n \n-int merge_trees(struct merge_options *o,\n+int merge_trees(struct merge_options *opt,\n \t\tstruct tree *head,\n \t\tstruct tree *merge,\n \t\tstruct tree *common,\n \t\tstruct tree **result)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \tint code, clean;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (!o->call_depth && repo_index_has_changes(o->repo, head, &sb)) {\n-\t\terr(o, _(\"Your local changes to the following files would be overwritten by merge:\\n  %s\"),\n+\tif (!opt->call_depth && repo_index_has_changes(opt->repo, head, &sb)) {\n+\t\terr(opt, _(\"Your local changes to the following files would be overwritten by merge:\\n  %s\"),\n \t\t    sb.buf);\n \t\treturn -1;\n \t}\n \n-\tif (o->subtree_shift) {\n-\t\tmerge = shift_tree_object(o->repo, head, merge, o->subtree_shift);\n-\t\tcommon = shift_tree_object(o->repo, head, common, o->subtree_shift);\n+\tif (opt->subtree_shift) {\n+\t\tmerge = shift_tree_object(opt->repo, head, merge, opt->subtree_shift);\n+\t\tcommon = shift_tree_object(opt->repo, head, common, opt->subtree_shift);\n \t}\n \n \tif (oid_eq(&common->object.oid, &merge->object.oid)) {\n-\t\toutput(o, 0, _(\"Already up to date!\"));\n+\t\toutput(opt, 0, _(\"Already up to date!\"));\n \t\t*result = head;\n \t\treturn 1;\n \t}\n \n-\tcode = unpack_trees_start(o, common, head, merge);\n+\tcode = unpack_trees_start(opt, common, head, merge);\n \n \tif (code != 0) {\n-\t\tif (show(o, 4) || o->call_depth)\n-\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n+\t\tif (show(opt, 4) || opt->call_depth)\n+\t\t\terr(opt, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\tunpack_trees_finish(o);\n+\t\tunpack_trees_finish(opt);\n \t\treturn -1;\n \t}\n \n@@ -3447,21 +3447,21 @@ int merge_trees(struct merge_options *o,\n \t\t * opposed to decaring a local hashmap is for convenience\n \t\t * so that we don't have to pass it to around.\n \t\t */\n-\t\thashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);\n-\t\tget_files_dirs(o, head);\n-\t\tget_files_dirs(o, merge);\n+\t\thashmap_init(&opt->current_file_dir_set, path_hashmap_cmp, NULL, 512);\n+\t\tget_files_dirs(opt, head);\n+\t\tget_files_dirs(opt, merge);\n \n-\t\tentries = get_unmerged(o->repo->index);\n-\t\tclean = detect_and_process_renames(o, common, head, merge,\n+\t\tentries = get_unmerged(opt->repo->index);\n+\t\tclean = detect_and_process_renames(opt, common, head, merge,\n \t\t\t\t\t\t   entries, &re_info);\n-\t\trecord_df_conflict_files(o, entries);\n+\t\trecord_df_conflict_files(opt, entries);\n \t\tif (clean < 0)\n \t\t\tgoto cleanup;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed) {\n-\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tint ret = process_entry(opt, path, e);\n \t\t\t\tif (!ret)\n \t\t\t\t\tclean = 0;\n \t\t\t\telse if (ret < 0) {\n@@ -3483,19 +3483,19 @@ int merge_trees(struct merge_options *o,\n \t\tstring_list_clear(entries, 1);\n \t\tfree(entries);\n \n-\t\thashmap_free(&o->current_file_dir_set, 1);\n+\t\thashmap_free(&opt->current_file_dir_set, 1);\n \n \t\tif (clean < 0) {\n-\t\t\tunpack_trees_finish(o);\n+\t\t\tunpack_trees_finish(opt);\n \t\t\treturn clean;\n \t\t}\n \t}\n \telse\n \t\tclean = 1;\n \n-\tunpack_trees_finish(o);\n+\tunpack_trees_finish(opt);\n \n-\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\tif (opt->call_depth && !(*result = write_tree_from_memory(opt)))\n \t\treturn -1;\n \n \treturn clean;\n@@ -3516,7 +3516,7 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)\n  * Merge the commits h1 and h2, return the resulting virtual\n  * commit object and a flag indicating the cleanness of the merge.\n  */\n-int merge_recursive(struct merge_options *o,\n+int merge_recursive(struct merge_options *opt,\n \t\t    struct commit *h1,\n \t\t    struct commit *h2,\n \t\t    struct commit_list *ca,\n@@ -3527,10 +3527,10 @@ int merge_recursive(struct merge_options *o,\n \tstruct tree *mrtree;\n \tint clean;\n \n-\tif (show(o, 4)) {\n-\t\toutput(o, 4, _(\"Merging:\"));\n-\t\toutput_commit_title(o, h1);\n-\t\toutput_commit_title(o, h2);\n+\tif (show(opt, 4)) {\n+\t\toutput(opt, 4, _(\"Merging:\"));\n+\t\toutput_commit_title(opt, h1);\n+\t\toutput_commit_title(opt, h2);\n \t}\n \n \tif (!ca) {\n@@ -3538,13 +3538,13 @@ int merge_recursive(struct merge_options *o,\n \t\tca = reverse_commit_list(ca);\n \t}\n \n-\tif (show(o, 5)) {\n+\tif (show(opt, 5)) {\n \t\tunsigned cnt = commit_list_count(ca);\n \n-\t\toutput(o, 5, Q_(\"found %u common ancestor:\",\n+\t\toutput(opt, 5, Q_(\"found %u common ancestor:\",\n \t\t\t\t\"found %u common ancestors:\", cnt), cnt);\n \t\tfor (iter = ca; iter; iter = iter->next)\n-\t\t\toutput_commit_title(o, iter->item);\n+\t\t\toutput_commit_title(opt, iter->item);\n \t}\n \n \tmerged_common_ancestors = pop_commit(&ca);\n@@ -3552,13 +3552,13 @@ int merge_recursive(struct merge_options *o,\n \t\t/* if there is no common ancestor, use an empty tree */\n \t\tstruct tree *tree;\n \n-\t\ttree = lookup_tree(o->repo, o->repo->hash_algo->empty_tree);\n-\t\tmerged_common_ancestors = make_virtual_commit(o->repo, tree, \"ancestor\");\n+\t\ttree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);\n+\t\tmerged_common_ancestors = make_virtual_commit(opt->repo, tree, \"ancestor\");\n \t}\n \n \tfor (iter = ca; iter; iter = iter->next) {\n \t\tconst char *saved_b1, *saved_b2;\n-\t\to->call_depth++;\n+\t\topt->call_depth++;\n \t\t/*\n \t\t * When the merge fails, the result contains files\n \t\t * with conflict markers. The cleanness flag is\n@@ -3567,46 +3567,46 @@ int merge_recursive(struct merge_options *o,\n \t\t * overwritten it: the committed \"conflicts\" were\n \t\t * already resolved.\n \t\t */\n-\t\tdiscard_index(o->repo->index);\n-\t\tsaved_b1 = o->branch1;\n-\t\tsaved_b2 = o->branch2;\n-\t\to->branch1 = \"Temporary merge branch 1\";\n-\t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\tdiscard_index(opt->repo->index);\n+\t\tsaved_b1 = opt->branch1;\n+\t\tsaved_b2 = opt->branch2;\n+\t\topt->branch1 = \"Temporary merge branch 1\";\n+\t\topt->branch2 = \"Temporary merge branch 2\";\n+\t\tif (merge_recursive(opt, merged_common_ancestors, iter->item,\n \t\t\t\t    NULL, &merged_common_ancestors) < 0)\n \t\t\treturn -1;\n-\t\to->branch1 = saved_b1;\n-\t\to->branch2 = saved_b2;\n-\t\to->call_depth--;\n+\t\topt->branch1 = saved_b1;\n+\t\topt->branch2 = saved_b2;\n+\t\topt->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn err(o, _(\"merge returned no commit\"));\n+\t\t\treturn err(opt, _(\"merge returned no commit\"));\n \t}\n \n-\tdiscard_index(o->repo->index);\n-\tif (!o->call_depth)\n-\t\trepo_read_index(o->repo);\n+\tdiscard_index(opt->repo->index);\n+\tif (!opt->call_depth)\n+\t\trepo_read_index(opt->repo);\n \n-\to->ancestor = \"merged common ancestors\";\n-\tclean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),\n+\topt->ancestor = \"merged common ancestors\";\n+\tclean = merge_trees(opt, get_commit_tree(h1), get_commit_tree(h2),\n \t\t\t    get_commit_tree(merged_common_ancestors),\n \t\t\t    &mrtree);\n \tif (clean < 0) {\n-\t\tflush_output(o);\n+\t\tflush_output(opt);\n \t\treturn clean;\n \t}\n \n-\tif (o->call_depth) {\n-\t\t*result = make_virtual_commit(o->repo, mrtree, \"merged tree\");\n+\tif (opt->call_depth) {\n+\t\t*result = make_virtual_commit(opt->repo, mrtree, \"merged tree\");\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n-\tif (!o->call_depth && o->buffer_output < 2)\n-\t\tstrbuf_release(&o->obuf);\n-\tif (show(o, 2))\n+\tflush_output(opt);\n+\tif (!opt->call_depth && opt->buffer_output < 2)\n+\t\tstrbuf_release(&opt->obuf);\n+\tif (show(opt, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n-\t\t\t\t       o->needed_rename_limit, 0);\n+\t\t\t\t       opt->needed_rename_limit, 0);\n \treturn clean;\n }\n \n@@ -3628,7 +3628,7 @@ static struct commit *get_ref(struct repository *repo, const struct object_id *o\n \treturn (struct commit *)object;\n }\n \n-int merge_recursive_generic(struct merge_options *o,\n+int merge_recursive_generic(struct merge_options *opt,\n \t\t\t    const struct object_id *head,\n \t\t\t    const struct object_id *merge,\n \t\t\t    int num_base_list,\n@@ -3637,127 +3637,127 @@ int merge_recursive_generic(struct merge_options *o,\n {\n \tint clean;\n \tstruct lock_file lock = LOCK_INIT;\n-\tstruct commit *head_commit = get_ref(o->repo, head, o->branch1);\n-\tstruct commit *next_commit = get_ref(o->repo, merge, o->branch2);\n+\tstruct commit *head_commit = get_ref(opt->repo, head, opt->branch1);\n+\tstruct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);\n \tstruct commit_list *ca = NULL;\n \n \tif (base_list) {\n \t\tint i;\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n-\t\t\tif (!(base = get_ref(o->repo, base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n+\t\t\tif (!(base = get_ref(opt->repo, base_list[i], oid_to_hex(base_list[i]))))\n+\t\t\t\treturn err(opt, _(\"Could not parse object '%s'\"),\n \t\t\t\t\t   oid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n \t}\n \n-\trepo_hold_locked_index(o->repo, &lock, LOCK_DIE_ON_ERROR);\n-\tclean = merge_recursive(o, head_commit, next_commit, ca,\n+\trepo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);\n+\tclean = merge_recursive(opt, head_commit, next_commit, ca,\n \t\t\t\tresult);\n \tif (clean < 0) {\n \t\trollback_lock_file(&lock);\n \t\treturn clean;\n \t}\n \n-\tif (write_locked_index(o->repo->index, &lock,\n+\tif (write_locked_index(opt->repo->index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n-\t\treturn err(o, _(\"Unable to write index.\"));\n+\t\treturn err(opt, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n \n-static void merge_recursive_config(struct merge_options *o)\n+static void merge_recursive_config(struct merge_options *opt)\n {\n \tchar *value = NULL;\n-\tgit_config_get_int(\"merge.verbosity\", &o->verbosity);\n-\tgit_config_get_int(\"diff.renamelimit\", &o->diff_rename_limit);\n-\tgit_config_get_int(\"merge.renamelimit\", &o->merge_rename_limit);\n+\tgit_config_get_int(\"merge.verbosity\", &opt->verbosity);\n+\tgit_config_get_int(\"diff.renamelimit\", &opt->diff_rename_limit);\n+\tgit_config_get_int(\"merge.renamelimit\", &opt->merge_rename_limit);\n \tif (!git_config_get_string(\"diff.renames\", &value)) {\n-\t\to->diff_detect_rename = git_config_rename(\"diff.renames\", value);\n+\t\topt->diff_detect_rename = git_config_rename(\"diff.renames\", value);\n \t\tfree(value);\n \t}\n \tif (!git_config_get_string(\"merge.renames\", &value)) {\n-\t\to->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n+\t\topt->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n \t\tfree(value);\n \t}\n \tgit_config(git_xmerge_config, NULL);\n }\n \n-void init_merge_options(struct merge_options *o,\n+void init_merge_options(struct merge_options *opt,\n \t\t\tstruct repository *repo)\n {\n \tconst char *merge_verbosity;\n-\tmemset(o, 0, sizeof(struct merge_options));\n-\to->repo = repo;\n-\to->verbosity = 2;\n-\to->buffer_output = 1;\n-\to->diff_rename_limit = -1;\n-\to->merge_rename_limit = -1;\n-\to->renormalize = 0;\n-\to->diff_detect_rename = -1;\n-\to->merge_detect_rename = -1;\n-\to->detect_directory_renames = 1;\n-\tmerge_recursive_config(o);\n+\tmemset(opt, 0, sizeof(struct merge_options));\n+\topt->repo = repo;\n+\topt->verbosity = 2;\n+\topt->buffer_output = 1;\n+\topt->diff_rename_limit = -1;\n+\topt->merge_rename_limit = -1;\n+\topt->renormalize = 0;\n+\topt->diff_detect_rename = -1;\n+\topt->merge_detect_rename = -1;\n+\topt->detect_directory_renames = 1;\n+\tmerge_recursive_config(opt);\n \tmerge_verbosity = getenv(\"GIT_MERGE_VERBOSITY\");\n \tif (merge_verbosity)\n-\t\to->verbosity = strtol(merge_verbosity, NULL, 10);\n-\tif (o->verbosity >= 5)\n-\t\to->buffer_output = 0;\n-\tstrbuf_init(&o->obuf, 0);\n-\tstring_list_init(&o->df_conflict_file_set, 1);\n+\t\topt->verbosity = strtol(merge_verbosity, NULL, 10);\n+\tif (opt->verbosity >= 5)\n+\t\topt->buffer_output = 0;\n+\tstrbuf_init(&opt->obuf, 0);\n+\tstring_list_init(&opt->df_conflict_file_set, 1);\n }\n \n-int parse_merge_opt(struct merge_options *o, const char *s)\n+int parse_merge_opt(struct merge_options *opt, const char *s)\n {\n \tconst char *arg;\n \n \tif (!s || !*s)\n \t\treturn -1;\n \tif (!strcmp(s, \"ours\"))\n-\t\to->recursive_variant = MERGE_RECURSIVE_OURS;\n+\t\topt->recursive_variant = MERGE_RECURSIVE_OURS;\n \telse if (!strcmp(s, \"theirs\"))\n-\t\to->recursive_variant = MERGE_RECURSIVE_THEIRS;\n+\t\topt->recursive_variant = MERGE_RECURSIVE_THEIRS;\n \telse if (!strcmp(s, \"subtree\"))\n-\t\to->subtree_shift = \"\";\n+\t\topt->subtree_shift = \"\";\n \telse if (skip_prefix(s, \"subtree=\", &arg))\n-\t\to->subtree_shift = arg;\n+\t\topt->subtree_shift = arg;\n \telse if (!strcmp(s, \"patience\"))\n-\t\to->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);\n+\t\topt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);\n \telse if (!strcmp(s, \"histogram\"))\n-\t\to->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);\n+\t\topt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);\n \telse if (skip_prefix(s, \"diff-algorithm=\", &arg)) {\n \t\tlong value = parse_algorithm_value(arg);\n \t\tif (value < 0)\n \t\t\treturn -1;\n \t\t/* clear out previous settings */\n-\t\tDIFF_XDL_CLR(o, NEED_MINIMAL);\n-\t\to->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;\n-\t\to->xdl_opts |= value;\n+\t\tDIFF_XDL_CLR(opt, NEED_MINIMAL);\n+\t\topt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;\n+\t\topt->xdl_opts |= value;\n \t}\n \telse if (!strcmp(s, \"ignore-space-change\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(s, \"ignore-all-space\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE);\n \telse if (!strcmp(s, \"ignore-space-at-eol\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);\n \telse if (!strcmp(s, \"ignore-cr-at-eol\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_CR_AT_EOL);\n+\t\tDIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);\n \telse if (!strcmp(s, \"renormalize\"))\n-\t\to->renormalize = 1;\n+\t\topt->renormalize = 1;\n \telse if (!strcmp(s, \"no-renormalize\"))\n-\t\to->renormalize = 0;\n+\t\topt->renormalize = 0;\n \telse if (!strcmp(s, \"no-renames\"))\n-\t\to->merge_detect_rename = 0;\n+\t\topt->merge_detect_rename = 0;\n \telse if (!strcmp(s, \"find-renames\")) {\n-\t\to->merge_detect_rename = 1;\n-\t\to->rename_score = 0;\n+\t\topt->merge_detect_rename = 1;\n+\t\topt->rename_score = 0;\n \t}\n \telse if (skip_prefix(s, \"find-renames=\", &arg) ||\n \t\t skip_prefix(s, \"rename-threshold=\", &arg)) {\n-\t\tif ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)\n+\t\tif ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)\n \t\t\treturn -1;\n-\t\to->merge_detect_rename = 1;\n+\t\topt->merge_detect_rename = 1;\n \t}\n \t/*\n \t * Please update $__git_merge_strategy_options in\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372754","messageId":"20190330003336.21940-6-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 05/15] merge-recursive: use 'ci' for rename_conflict_info variable name","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:26Z","receivedAt":"2019-03-30T00:34:32Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"We used a couple different names, but used 'ci' the most.  Use the same\nvariable name throughout for a little extra consistency.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 41 ++++++++++++++++++-----------------------\n 1 file changed, 18 insertions(+), 23 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 4ed1b48630..ea5646debd 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -3098,7 +3098,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n \t\t\t\tstruct object_id *a_oid, int a_mode,\n \t\t\t\tstruct object_id *b_oid, int b_mode,\n-\t\t\t\tstruct rename_conflict_info *rename_conflict_info)\n+\t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n \tconst char *path1 = NULL, *path2 = NULL;\n@@ -3118,17 +3118,17 @@ static int handle_content_merge(struct merge_options *opt,\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n \n-\tif (rename_conflict_info) {\n-\t\tstruct diff_filepair *pair1 = rename_conflict_info->pair1;\n+\tif (ci) {\n+\t\tstruct diff_filepair *pair1 = ci->pair1;\n \n-\t\tpath1 = (opt->branch1 == rename_conflict_info->branch1) ?\n+\t\tpath1 = (opt->branch1 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If rename_conflict_info->pair2 != NULL, we are in\n+\t\t/* If ci->pair2 != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n-\t\tpath2 = (rename_conflict_info->pair2 ||\n-\t\t\t opt->branch2 == rename_conflict_info->branch1) ?\n+\t\tpath2 = (ci->pair2 ||\n+\t\t\t opt->branch2 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n@@ -3180,7 +3180,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n-\t\tif (rename_conflict_info && !df_conflict_remains)\n+\t\tif (ci && !df_conflict_remains)\n \t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n@@ -3206,7 +3206,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(opt, path, rename_conflict_info->branch1);\n+\t\tnew_path = unique_path(opt, path, ci->branch1);\n \t\tif (is_dirty) {\n \t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n@@ -3251,8 +3251,8 @@ static int process_entry(struct merge_options *opt,\n \n \tentry->processed = 1;\n \tif (entry->rename_conflict_info) {\n-\t\tstruct rename_conflict_info *conflict_info = entry->rename_conflict_info;\n-\t\tswitch (conflict_info->rename_type) {\n+\t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n+\t\tswitch (ci->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n \t\t\tclean_merge = handle_rename_normal(opt,\n@@ -3260,13 +3260,11 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n \t\t\t\t\t\t\t   b_oid, b_mode,\n-\t\t\t\t\t\t\t   conflict_info);\n+\t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt,\n-\t\t\t\t\t\t  conflict_info->pair1,\n-\t\t\t\t\t\t  conflict_info->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->pair1, ci->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3276,19 +3274,17 @@ static int process_entry(struct merge_options *opt,\n \t\t\t * two-way merged cleanly with the added file, I\n \t\t\t * guess it's a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_add(opt, conflict_info);\n+\t\t\tclean_merge = handle_rename_add(opt, ci);\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt,\n-\t\t\t\t\t\t conflict_info->pair1,\n-\t\t\t\t\t\t conflict_info->branch1,\n-\t\t\t\t\t\t conflict_info->branch2))\n+\t\t\tif (handle_rename_delete(opt, ci->pair1,\n+\t\t\t\t\t\t ci->branch1, ci->branch2))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_rename_1to2(opt, conflict_info))\n+\t\t\tif (handle_rename_rename_1to2(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n@@ -3298,8 +3294,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\t * can then be two-way merged cleanly, I guess it's\n \t\t\t * a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_rename_2to1(opt,\n-\t\t\t\t\t\t\t\tconflict_info);\n+\t\t\tclean_merge = handle_rename_rename_2to1(opt, ci);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372755","messageId":"20190330003336.21940-4-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 03/15] merge-recursive: rename diff_filespec 'one' to 'o'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:24Z","receivedAt":"2019-03-30T00:34:35Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"In the previous commit, we noted that several places throughout merge\nrecursive both had a reason to use 'o'; some for a merge_options struct,\nand others for a diff_filespec struct.  Some places had both, forcing\none of the two to be renamed, though the choice was inconsistent.  Now\nthat the merge_options struct has been renamed to 'opt' everywhere, we\ncan replace the few places that used 'one' for the diff_filespec to 'o'.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 46 +++++++++++++++++++++++-----------------------\n 1 file changed, 23 insertions(+), 23 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 09b76d596e..36af5d9cc6 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1050,7 +1050,7 @@ struct merge_file_info {\n \n static int merge_3way(struct merge_options *opt,\n \t\t      mmbuffer_t *result_buf,\n-\t\t      const struct diff_filespec *one,\n+\t\t      const struct diff_filespec *o,\n \t\t      const struct diff_filespec *a,\n \t\t      const struct diff_filespec *b,\n \t\t      const char *branch1,\n@@ -1084,9 +1084,9 @@ static int merge_3way(struct merge_options *opt,\n \t}\n \n \tif (strcmp(a->path, b->path) ||\n-\t    (opt->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n+\t    (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {\n \t\tbase_name = opt->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, one->path);\n+\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, o->path);\n \t\tname1 = mkpathdup(\"%s:%s\", branch1, a->path);\n \t\tname2 = mkpathdup(\"%s:%s\", branch2, b->path);\n \t} else {\n@@ -1096,7 +1096,7 @@ static int merge_3way(struct merge_options *opt,\n \t\tname2 = mkpathdup(\"%s\", branch2);\n \t}\n \n-\tread_mmblob(&orig, &one->oid);\n+\tread_mmblob(&orig, &o->oid);\n \tread_mmblob(&src1, &a->oid);\n \tread_mmblob(&src2, &b->oid);\n \n@@ -1295,7 +1295,7 @@ static int merge_submodule(struct merge_options *opt,\n }\n \n static int merge_mode_and_contents(struct merge_options *opt,\n-\t\t\t\t   const struct diff_filespec *one,\n+\t\t\t\t   const struct diff_filespec *o,\n \t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t   const char *filename,\n@@ -1310,7 +1310,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t * side of the conflict markers and the other branch on the\n \t\t * top.  Fix that.\n \t\t */\n-\t\treturn merge_mode_and_contents(opt, one, b, a,\n+\t\treturn merge_mode_and_contents(opt, o, b, a,\n \t\t\t\t\t       filename,\n \t\t\t\t\t       branch2, branch1,\n \t\t\t\t\t       extra_marker_size, result);\n@@ -1329,31 +1329,31 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n-\t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n+\t\tif (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))\n \t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n-\t\tif (a->mode == b->mode || a->mode == one->mode)\n+\t\tif (a->mode == b->mode || a->mode == o->mode)\n \t\t\tresult->mode = b->mode;\n \t\telse {\n \t\t\tresult->mode = a->mode;\n-\t\t\tif (b->mode != one->mode) {\n+\t\t\tif (b->mode != o->mode) {\n \t\t\t\tresult->clean = 0;\n \t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n-\t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n+\t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))\n \t\t\toidcpy(&result->oid, &b->oid);\n-\t\telse if (oid_eq(&b->oid, &one->oid))\n+\t\telse if (oid_eq(&b->oid, &o->oid))\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n \n-\t\t\tmerge_status = merge_3way(opt, &result_buf, one, a, b,\n+\t\t\tmerge_status = merge_3way(opt, &result_buf, o, a, b,\n \t\t\t\t\t\t  branch1, branch2,\n \t\t\t\t\t\t  extra_marker_size);\n \n@@ -1372,8 +1372,8 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(opt, &result->oid,\n-\t\t\t\t\t\t\tone->path,\n-\t\t\t\t\t\t\t&one->oid,\n+\t\t\t\t\t\t\to->path,\n+\t\t\t\t\t\t\t&o->oid,\n \t\t\t\t\t\t\t&a->oid,\n \t\t\t\t\t\t\t&b->oid);\n \t\t} else if (S_ISLNK(a->mode)) {\n@@ -1750,7 +1750,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \tstruct merge_file_info mfi;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n-\tstruct diff_filespec *one = ci->pair1->one;\n+\tstruct diff_filespec *o = ci->pair1->one;\n \tstruct diff_filespec *a = ci->pair1->two;\n \tstruct diff_filespec *b = ci->pair2->two;\n \tchar *path_desc;\n@@ -1758,13 +1758,13 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n-\t       one->path, a->path, ci->branch1,\n-\t       one->path, b->path, ci->branch2,\n+\t       o->path, a->path, ci->branch1,\n+\t       o->path, b->path, ci->branch2,\n \t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n-\t\t\t    a->path, b->path, one->path);\n-\tif (merge_mode_and_contents(opt, one, a, b, path_desc,\n+\t\t\t    a->path, b->path, o->path);\n+\tif (merge_mode_and_contents(opt, o, a, b, path_desc,\n \t\t\t\t    ci->branch1, ci->branch2,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -1777,7 +1777,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, one->path))\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, o->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -2863,10 +2863,10 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (clean_merge < 0)\n \t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n-\t\t\t\tstruct diff_filespec *one, *a, *b;\n+\t\t\t\tstruct diff_filespec *o, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n \n-\t\t\t\tone = ren1->pair->one;\n+\t\t\t\to = ren1->pair->one;\n \t\t\t\tif (a_renames == renames1) {\n \t\t\t\t\ta = ren1->pair->two;\n \t\t\t\t\tb = &src_other;\n@@ -2874,7 +2874,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\tb = ren1->pair->two;\n \t\t\t\t\ta = &src_other;\n \t\t\t\t}\n-\t\t\t\tupdate_entry(ren1->dst_entry, one, a, b);\n+\t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n \t\t\t\t\t\t\t   ren1->pair,\n \t\t\t\t\t\t\t   NULL,\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372756","messageId":"20190330003336.21940-7-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v2 06/15] merge-recursive: move some struct declarations together","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:27Z","receivedAt":"2019-03-30T00:34:36Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"These structs are related and reference each other, so move them\ntogether to make it easier for folks to determine what they hold and\nwhat their purpose is.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 78 +++++++++++++++++++++++------------------------\n 1 file changed, 39 insertions(+), 39 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ea5646debd..c4a2ef2a37 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -196,18 +196,6 @@ enum rename_type {\n \tRENAME_TWO_FILES_TO_ONE\n };\n \n-struct rename_conflict_info {\n-\tenum rename_type rename_type;\n-\tstruct diff_filepair *pair1;\n-\tstruct diff_filepair *pair2;\n-\tconst char *branch1;\n-\tconst char *branch2;\n-\tstruct stage_data *dst_entry1;\n-\tstruct stage_data *dst_entry2;\n-\tstruct diff_filespec ren1_other;\n-\tstruct diff_filespec ren2_other;\n-};\n-\n /*\n  * Since we want to write the index eventually, we cannot reuse the index\n  * for these (temporary) data.\n@@ -221,6 +209,45 @@ struct stage_data {\n \tunsigned processed:1;\n };\n \n+struct rename {\n+\tstruct diff_filepair *pair;\n+\t/*\n+\t * Purpose of src_entry and dst_entry:\n+\t *\n+\t * If 'before' is renamed to 'after' then src_entry will contain\n+\t * the versions of 'before' from the merge_base, HEAD, and MERGE in\n+\t * stages 1, 2, and 3; dst_entry will contain the respective\n+\t * versions of 'after' in corresponding locations.  Thus, we have a\n+\t * total of six modes and oids, though some will be null.  (Stage 0\n+\t * is ignored; we're interested in handling conflicts.)\n+\t *\n+\t * Since we don't turn on break-rewrites by default, neither\n+\t * src_entry nor dst_entry can have all three of their stages have\n+\t * non-null oids, meaning at most four of the six will be non-null.\n+\t * Also, since this is a rename, both src_entry and dst_entry will\n+\t * have at least one non-null oid, meaning at least two will be\n+\t * non-null.  Of the six oids, a typical rename will have three be\n+\t * non-null.  Only two implies a rename/delete, and four implies a\n+\t * rename/add.\n+\t */\n+\tstruct stage_data *src_entry;\n+\tstruct stage_data *dst_entry;\n+\tunsigned add_turned_into_rename:1;\n+\tunsigned processed:1;\n+};\n+\n+struct rename_conflict_info {\n+\tenum rename_type rename_type;\n+\tstruct diff_filepair *pair1;\n+\tstruct diff_filepair *pair2;\n+\tconst char *branch1;\n+\tconst char *branch2;\n+\tstruct stage_data *dst_entry1;\n+\tstruct stage_data *dst_entry2;\n+\tstruct diff_filespec ren1_other;\n+\tstruct diff_filespec ren2_other;\n+};\n+\n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct diff_filepair *pair1,\n \t\t\t\t\t      struct diff_filepair *pair2,\n@@ -645,33 +672,6 @@ static void record_df_conflict_files(struct merge_options *opt,\n \tstring_list_clear(&df_sorted_entries, 0);\n }\n \n-struct rename {\n-\tstruct diff_filepair *pair;\n-\t/*\n-\t * Purpose of src_entry and dst_entry:\n-\t *\n-\t * If 'before' is renamed to 'after' then src_entry will contain\n-\t * the versions of 'before' from the merge_base, HEAD, and MERGE in\n-\t * stages 1, 2, and 3; dst_entry will contain the respective\n-\t * versions of 'after' in corresponding locations.  Thus, we have a\n-\t * total of six modes and oids, though some will be null.  (Stage 0\n-\t * is ignored; we're interested in handling conflicts.)\n-\t *\n-\t * Since we don't turn on break-rewrites by default, neither\n-\t * src_entry nor dst_entry can have all three of their stages have\n-\t * non-null oids, meaning at most four of the six will be non-null.\n-\t * Also, since this is a rename, both src_entry and dst_entry will\n-\t * have at least one non-null oid, meaning at least two will be\n-\t * non-null.  Of the six oids, a typical rename will have three be\n-\t * non-null.  Only two implies a rename/delete, and four implies a\n-\t * rename/add.\n-\t */\n-\tstruct stage_data *src_entry;\n-\tstruct stage_data *dst_entry;\n-\tunsigned add_turned_into_rename:1;\n-\tunsigned processed:1;\n-};\n-\n static int update_stages(struct merge_options *opt, const char *path,\n \t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n-- \n2.21.0.211.g078f4bef79.dirty\n\n"},{"id":"372757","messageId":"20190330003336.21940-1-newren@gmail.com","threadId":"50603","inReplyTo":"20190306002744.14418-1-newren@gmail.com","subject":"[PATCH v2 00/15] Switch directory rename detection default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-03-30T00:33:21Z","receivedAt":"2019-03-30T00:34:38Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"I know this is kinda big, but it's mostly simple mechanical cleanups.\nStuff I'd like reviewers to focus on:\n  * Patch 15, particularly looking over the new testcases (13a-13d) in\n    t6043 and the documentation.\n  * Should I have switched 'unsigned short' to 'unsigned' instead of\n    vice-versa in patch 1?\n  * Similarly, does anyone have a reason to prefer oid,mode pair over\n    using a diff_filespec as I did in patch 11?\n\nThe crux of this series is in patch 15, changing the default for\ndirectory rename detection to treat it as a conflict.  It is similar\nto the patch Junio previously reviewed; changes:\n\n  * It now also handles files renamed into directories on one side\n    where the other side renamed the directory away.  (Previously, it\n    only handled new files added into such directories.)\n    \n  * As suggested by Junio, even if merge.directoryRenames=true is set\n    in config (to accept directory rename heuristics as correct\n    without reporting conflicts), messages will now be printed about\n    the paths being changed by directory renames.\n    \n  * Error and info messages updated based in part on Junio's\n    suggestions, a second check that the messages make sense would be\n    useful.\n  \n  * There are now testcases testing the conflict and information\n    message output (testcases 13[abcd] in t6043)\n\n\nThe reason this series exploded from one patch to fifteen was the need\nto handle transitive renames (renames into directories on one side\nwhile the other side renamed that directory away).  For that, I needed\nto plumb more information through...but quickly got frustrated with\nvarious ugly code bits and went down a rabbit hole.\n\nThe first 12 patches are solely cleanups\n\nPatches 13 & 14 are simple changes to make additional information\navailable.\n\nPatch 15 is primary purpose of the series, described above.\n\n\nElijah Newren (15):\n  Use 'unsigned short' for mode, like diff_filespec does\n  merge-recursive: rename merge_options argument from 'o' to 'opt'\n  merge-recursive: rename diff_filespec 'one' to 'o'\n  merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'\n  merge-recursive: use 'ci' for rename_conflict_info variable name\n  merge-recursive: move some struct declarations together\n  merge-recursive: shrink rename_conflict_info\n  merge-recursive: remove ren[12]_other fields from rename_conflict_info\n  merge-recursive: track branch where rename occurred in rename struct\n  merge-recursive: cleanup handle_rename_* function signatures\n  merge-recursive: switch from (oid,mode) pairs to a diff_filespec\n  t6043: fix copied test description to match its purpose\n  merge-recursive: track information associated with directory renames\n  merge-recursive: give callers of handle_content_merge() access to\n    contents\n  merge-recursive: switch directory rename detection default\n\n Documentation/config/merge.txt         |   19 +-\n archive.c                              |    2 +-\n blame.c                                |    2 +-\n blame.h                                |    2 +-\n builtin/rm.c                           |    2 +-\n builtin/update-index.c                 |    2 +-\n cache.h                                |    2 +-\n fsck.c                                 |    2 +-\n line-log.c                             |    2 +-\n match-trees.c                          |    8 +-\n merge-recursive.c                      | 1853 ++++++++++++------------\n notes.c                                |    2 +-\n sha1-name.c                            |    2 +-\n t/t3401-rebase-and-am-rename.sh        |    8 +-\n t/t6043-merge-rename-directories.sh    |  462 +++++-\n t/t6046-merge-skip-unneeded-updates.sh |    8 +-\n tree-diff.c                            |    2 +-\n tree-walk.c                            |    6 +-\n tree-walk.h                            |    6 +-\n 19 files changed, 1367 insertions(+), 1025 deletions(-)\n\n-- \n2.21.0.211.gb523892db9\n\n"},{"id":"372764","messageId":"87imw0afp4.fsf@evledraar.gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-16-newren@gmail.com","subject":"Re: [PATCH v2 15/15] merge-recursive: switch directory rename detection default","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-03-30T09:12:39Z","receivedAt":"2019-03-30T09:12:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Mar 30 2019, Elijah Newren wrote:\n\nI may have more, just quickly skimming this for the first time...\n\n>  merge.renames::\n> -\tWhether and how Git detects renames.  If set to \"false\",\n> -\trename detection is disabled. If set to \"true\", basic rename\n> -\tdetection is enabled.  Defaults to the value of diff.renames.\n> +\tWhether Git detects renames.  If set to \"false\", rename detection\n> +\tis disabled. If set to \"true\", basic rename detection is enabled.\n> +\tDefaults to the value of diff.renames.\n> [...]\n> +\tif (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n> +\t\tif (!strcasecmp(value, \"true\"))\n> +\t\t\topt->detect_directory_renames = 2;\n> +\t\telse if (!strcasecmp(value, \"false\"))\n> +\t\t\topt->detect_directory_renames = 0;\n> +\t\telse if (!strcasecmp(value, \"conflict\"))\n> +\t\t\topt->detect_directory_renames = 1;\n> +\t\telse {\n> +\t\t\terror(_(\"Invalid value for merge.directoryRenames: %s\"),\n> +\t\t\t      value);\n> +\t\t\topt->detect_directory_renames = 1;\n> +\t\t}\n> +\t\tfree(value);\n> +\t}\n\nInstead of making your own true/false parser you can use\ngit_parse_maybe_bool(). See what we do for merge.ff:\n\n    builtin/merge.c-617-    else if (!strcmp(k, \"merge.ff\")) {\n    builtin/merge.c:618:            int boolval = git_parse_maybe_bool(v);\n    builtin/merge.c-619-            if (0 <= boolval) {\n    builtin/merge.c-620-                    fast_forward = boolval ? FF_ALLOW : FF_NO;\n    builtin/merge.c-621-            } else if (v && !strcmp(v, \"only\")) {\n    builtin/merge.c-622-                    fast_forward = FF_ONLY;\n    builtin/merge.c-623-            } /* do not barf on values from future versions of git */\n    builtin/merge.c-624-            return 0;\n\nSmall nit, but allows us to document \"this config takse bool or ...\"\nwithout having different verions of \"bool\" in various places.\n\nAlso, I don't care personally, but this also violates the \"if one thing\nrequires braces put it on all the if/else arms\" in CodingGuidelines.\n"},{"id":"372897","messageId":"CABPp-BEAMo-+mZcPTSyhts_-6eMcnRYc6bBojJSAtMTS2meHUg@mail.gmail.com","threadId":"50603","inReplyTo":"87imw0afp4.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 15/15] merge-recursive: switch directory rename detection default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-01T15:41:11Z","receivedAt":"2019-04-01T15:41:24Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 30, 2019 at 2:12 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Sat, Mar 30 2019, Elijah Newren wrote:\n>\n> I may have more, just quickly skimming this for the first time...\n>\n> >  merge.renames::\n> > -     Whether and how Git detects renames.  If set to \"false\",\n> > -     rename detection is disabled. If set to \"true\", basic rename\n> > -     detection is enabled.  Defaults to the value of diff.renames.\n> > +     Whether Git detects renames.  If set to \"false\", rename detection\n> > +     is disabled. If set to \"true\", basic rename detection is enabled.\n> > +     Defaults to the value of diff.renames.\n> > [...]\n> > +     if (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n> > +             if (!strcasecmp(value, \"true\"))\n> > +                     opt->detect_directory_renames = 2;\n> > +             else if (!strcasecmp(value, \"false\"))\n> > +                     opt->detect_directory_renames = 0;\n> > +             else if (!strcasecmp(value, \"conflict\"))\n> > +                     opt->detect_directory_renames = 1;\n> > +             else {\n> > +                     error(_(\"Invalid value for merge.directoryRenames: %s\"),\n> > +                           value);\n> > +                     opt->detect_directory_renames = 1;\n> > +             }\n> > +             free(value);\n> > +     }\n>\n> Instead of making your own true/false parser you can use\n> git_parse_maybe_bool(). See what we do for merge.ff:\n>\n>     builtin/merge.c-617-    else if (!strcmp(k, \"merge.ff\")) {\n>     builtin/merge.c:618:            int boolval = git_parse_maybe_bool(v);\n>     builtin/merge.c-619-            if (0 <= boolval) {\n>     builtin/merge.c-620-                    fast_forward = boolval ? FF_ALLOW : FF_NO;\n>     builtin/merge.c-621-            } else if (v && !strcmp(v, \"only\")) {\n>     builtin/merge.c-622-                    fast_forward = FF_ONLY;\n>     builtin/merge.c-623-            } /* do not barf on values from future versions of git */\n>     builtin/merge.c-624-            return 0;\n>\n> Small nit, but allows us to document \"this config takse bool or ...\"\n> without having different verions of \"bool\" in various places.\n>\n> Also, I don't care personally, but this also violates the \"if one thing\n> requires braces put it on all the if/else arms\" in CodingGuidelines.\n\nThanks for taking a look.  I'll make the fix, and wait for other\nfeedback before resending.\n"},{"id":"373203","messageId":"20190405150026.5260-4-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 03/15] merge-recursive: rename diff_filespec 'one' to 'o'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:14Z","receivedAt":"2019-04-05T15:00:58Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"In the previous commit, we noted that several places throughout merge\nrecursive both had a reason to use 'o'; some for a merge_options struct,\nand others for a diff_filespec struct.  Some places had both, forcing\none of the two to be renamed, though the choice was inconsistent.  Now\nthat the merge_options struct has been renamed to 'opt' everywhere, we\ncan replace the few places that used 'one' for the diff_filespec to 'o'.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 46 +++++++++++++++++++++++-----------------------\n 1 file changed, 23 insertions(+), 23 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 09b76d596e..36af5d9cc6 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1050,7 +1050,7 @@ struct merge_file_info {\n \n static int merge_3way(struct merge_options *opt,\n \t\t      mmbuffer_t *result_buf,\n-\t\t      const struct diff_filespec *one,\n+\t\t      const struct diff_filespec *o,\n \t\t      const struct diff_filespec *a,\n \t\t      const struct diff_filespec *b,\n \t\t      const char *branch1,\n@@ -1084,9 +1084,9 @@ static int merge_3way(struct merge_options *opt,\n \t}\n \n \tif (strcmp(a->path, b->path) ||\n-\t    (opt->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n+\t    (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {\n \t\tbase_name = opt->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, one->path);\n+\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, o->path);\n \t\tname1 = mkpathdup(\"%s:%s\", branch1, a->path);\n \t\tname2 = mkpathdup(\"%s:%s\", branch2, b->path);\n \t} else {\n@@ -1096,7 +1096,7 @@ static int merge_3way(struct merge_options *opt,\n \t\tname2 = mkpathdup(\"%s\", branch2);\n \t}\n \n-\tread_mmblob(&orig, &one->oid);\n+\tread_mmblob(&orig, &o->oid);\n \tread_mmblob(&src1, &a->oid);\n \tread_mmblob(&src2, &b->oid);\n \n@@ -1295,7 +1295,7 @@ static int merge_submodule(struct merge_options *opt,\n }\n \n static int merge_mode_and_contents(struct merge_options *opt,\n-\t\t\t\t   const struct diff_filespec *one,\n+\t\t\t\t   const struct diff_filespec *o,\n \t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t   const char *filename,\n@@ -1310,7 +1310,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t * side of the conflict markers and the other branch on the\n \t\t * top.  Fix that.\n \t\t */\n-\t\treturn merge_mode_and_contents(opt, one, b, a,\n+\t\treturn merge_mode_and_contents(opt, o, b, a,\n \t\t\t\t\t       filename,\n \t\t\t\t\t       branch2, branch1,\n \t\t\t\t\t       extra_marker_size, result);\n@@ -1329,31 +1329,31 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n-\t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n+\t\tif (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))\n \t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n-\t\tif (a->mode == b->mode || a->mode == one->mode)\n+\t\tif (a->mode == b->mode || a->mode == o->mode)\n \t\t\tresult->mode = b->mode;\n \t\telse {\n \t\t\tresult->mode = a->mode;\n-\t\t\tif (b->mode != one->mode) {\n+\t\t\tif (b->mode != o->mode) {\n \t\t\t\tresult->clean = 0;\n \t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n-\t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n+\t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))\n \t\t\toidcpy(&result->oid, &b->oid);\n-\t\telse if (oid_eq(&b->oid, &one->oid))\n+\t\telse if (oid_eq(&b->oid, &o->oid))\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n \n-\t\t\tmerge_status = merge_3way(opt, &result_buf, one, a, b,\n+\t\t\tmerge_status = merge_3way(opt, &result_buf, o, a, b,\n \t\t\t\t\t\t  branch1, branch2,\n \t\t\t\t\t\t  extra_marker_size);\n \n@@ -1372,8 +1372,8 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(opt, &result->oid,\n-\t\t\t\t\t\t\tone->path,\n-\t\t\t\t\t\t\t&one->oid,\n+\t\t\t\t\t\t\to->path,\n+\t\t\t\t\t\t\t&o->oid,\n \t\t\t\t\t\t\t&a->oid,\n \t\t\t\t\t\t\t&b->oid);\n \t\t} else if (S_ISLNK(a->mode)) {\n@@ -1750,7 +1750,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \tstruct merge_file_info mfi;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n-\tstruct diff_filespec *one = ci->pair1->one;\n+\tstruct diff_filespec *o = ci->pair1->one;\n \tstruct diff_filespec *a = ci->pair1->two;\n \tstruct diff_filespec *b = ci->pair2->two;\n \tchar *path_desc;\n@@ -1758,13 +1758,13 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n-\t       one->path, a->path, ci->branch1,\n-\t       one->path, b->path, ci->branch2,\n+\t       o->path, a->path, ci->branch1,\n+\t       o->path, b->path, ci->branch2,\n \t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n-\t\t\t    a->path, b->path, one->path);\n-\tif (merge_mode_and_contents(opt, one, a, b, path_desc,\n+\t\t\t    a->path, b->path, o->path);\n+\tif (merge_mode_and_contents(opt, o, a, b, path_desc,\n \t\t\t\t    ci->branch1, ci->branch2,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -1777,7 +1777,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, one->path))\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, o->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -2863,10 +2863,10 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (clean_merge < 0)\n \t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n-\t\t\t\tstruct diff_filespec *one, *a, *b;\n+\t\t\t\tstruct diff_filespec *o, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n \n-\t\t\t\tone = ren1->pair->one;\n+\t\t\t\to = ren1->pair->one;\n \t\t\t\tif (a_renames == renames1) {\n \t\t\t\t\ta = ren1->pair->two;\n \t\t\t\t\tb = &src_other;\n@@ -2874,7 +2874,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\tb = ren1->pair->two;\n \t\t\t\t\ta = &src_other;\n \t\t\t\t}\n-\t\t\t\tupdate_entry(ren1->dst_entry, one, a, b);\n+\t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n \t\t\t\t\t\t\t   ren1->pair,\n \t\t\t\t\t\t\t   NULL,\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373204","messageId":"20190405150026.5260-6-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 05/15] merge-recursive: use 'ci' for rename_conflict_info variable name","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:16Z","receivedAt":"2019-04-05T15:01:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"We used a couple different names, but used 'ci' the most.  Use the same\nvariable name throughout for a little extra consistency.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 41 ++++++++++++++++++-----------------------\n 1 file changed, 18 insertions(+), 23 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 4ed1b48630..ea5646debd 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -3098,7 +3098,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n \t\t\t\tstruct object_id *a_oid, int a_mode,\n \t\t\t\tstruct object_id *b_oid, int b_mode,\n-\t\t\t\tstruct rename_conflict_info *rename_conflict_info)\n+\t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n \tconst char *path1 = NULL, *path2 = NULL;\n@@ -3118,17 +3118,17 @@ static int handle_content_merge(struct merge_options *opt,\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n \n-\tif (rename_conflict_info) {\n-\t\tstruct diff_filepair *pair1 = rename_conflict_info->pair1;\n+\tif (ci) {\n+\t\tstruct diff_filepair *pair1 = ci->pair1;\n \n-\t\tpath1 = (opt->branch1 == rename_conflict_info->branch1) ?\n+\t\tpath1 = (opt->branch1 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If rename_conflict_info->pair2 != NULL, we are in\n+\t\t/* If ci->pair2 != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n-\t\tpath2 = (rename_conflict_info->pair2 ||\n-\t\t\t opt->branch2 == rename_conflict_info->branch1) ?\n+\t\tpath2 = (ci->pair2 ||\n+\t\t\t opt->branch2 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n@@ -3180,7 +3180,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n-\t\tif (rename_conflict_info && !df_conflict_remains)\n+\t\tif (ci && !df_conflict_remains)\n \t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n@@ -3206,7 +3206,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(opt, path, rename_conflict_info->branch1);\n+\t\tnew_path = unique_path(opt, path, ci->branch1);\n \t\tif (is_dirty) {\n \t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n@@ -3251,8 +3251,8 @@ static int process_entry(struct merge_options *opt,\n \n \tentry->processed = 1;\n \tif (entry->rename_conflict_info) {\n-\t\tstruct rename_conflict_info *conflict_info = entry->rename_conflict_info;\n-\t\tswitch (conflict_info->rename_type) {\n+\t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n+\t\tswitch (ci->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n \t\t\tclean_merge = handle_rename_normal(opt,\n@@ -3260,13 +3260,11 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n \t\t\t\t\t\t\t   b_oid, b_mode,\n-\t\t\t\t\t\t\t   conflict_info);\n+\t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt,\n-\t\t\t\t\t\t  conflict_info->pair1,\n-\t\t\t\t\t\t  conflict_info->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->pair1, ci->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3276,19 +3274,17 @@ static int process_entry(struct merge_options *opt,\n \t\t\t * two-way merged cleanly with the added file, I\n \t\t\t * guess it's a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_add(opt, conflict_info);\n+\t\t\tclean_merge = handle_rename_add(opt, ci);\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt,\n-\t\t\t\t\t\t conflict_info->pair1,\n-\t\t\t\t\t\t conflict_info->branch1,\n-\t\t\t\t\t\t conflict_info->branch2))\n+\t\t\tif (handle_rename_delete(opt, ci->pair1,\n+\t\t\t\t\t\t ci->branch1, ci->branch2))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_rename_1to2(opt, conflict_info))\n+\t\t\tif (handle_rename_rename_1to2(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n@@ -3298,8 +3294,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\t * can then be two-way merged cleanly, I guess it's\n \t\t\t * a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_rename_2to1(opt,\n-\t\t\t\t\t\t\t\tconflict_info);\n+\t\t\tclean_merge = handle_rename_rename_2to1(opt, ci);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373205","messageId":"20190405150026.5260-1-newren@gmail.com","threadId":"50603","inReplyTo":"20190330003336.21940-1-newren@gmail.com","subject":"[PATCH v3 00/15] Switch directory rename detection default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:11Z","receivedAt":"2019-04-05T15:01:01Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"This series adds a new configuration option, merge.directoryRenames,\nfor setting how to make use of directory rename detection heuristics.\nThe default becomes \"conflict\", meaning that conflicts are reported\ninstead of silently moving paths according to the heuristics.  Also,\neven when merge.directoryRenames config setting is \"true\", this series\nchanges behavior in that it now prints informational messages about\npaths that are adjusted by the directory rename detection heuristics.\n\nChanges since v2 (range-diff below):\n  * Made use of git_parse_maybe_bool() as suggested by Ævar, and made\n    the parsing of the merge.directoryRenames setting look more like\n    that for merge.ff.\n\nI didn't get much review of round 2, which maybe means everyone is\nhappy with what they see.  If anyone would like to take a look at just\npart of the series, the pieces I'd most like folks to look at are:\n  * Patch 15, particularly looking over the new testcases (13a-13d) in\n    t6043 and the documentation.\n  * Should I have switched the type of \"mode\" from 'unsigned short' to\n    'unsigned' instead of vice-versa in patch 1?\n  * Similarly, does anyone have a reason to prefer oid,mode pair over\n    using a diff_filespec (in patch 11 I convert half the sites to the\n    latter)?\n\n\nElijah Newren (15):\n  Use 'unsigned short' for mode, like diff_filespec does\n  merge-recursive: rename merge_options argument from 'o' to 'opt'\n  merge-recursive: rename diff_filespec 'one' to 'o'\n  merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'\n  merge-recursive: use 'ci' for rename_conflict_info variable name\n  merge-recursive: move some struct declarations together\n  merge-recursive: shrink rename_conflict_info\n  merge-recursive: remove ren[12]_other fields from rename_conflict_info\n  merge-recursive: track branch where rename occurred in rename struct\n  merge-recursive: cleanup handle_rename_* function signatures\n  merge-recursive: switch from (oid,mode) pairs to a diff_filespec\n  t6043: fix copied test description to match its purpose\n  merge-recursive: track information associated with directory renames\n  merge-recursive: give callers of handle_content_merge() access to\n    contents\n  merge-recursive: switch directory rename detection default\n\n Documentation/config/merge.txt         |   19 +-\n archive.c                              |    2 +-\n blame.c                                |    2 +-\n blame.h                                |    2 +-\n builtin/rm.c                           |    2 +-\n builtin/update-index.c                 |    2 +-\n cache.h                                |    2 +-\n fsck.c                                 |    2 +-\n line-log.c                             |    2 +-\n match-trees.c                          |    8 +-\n merge-recursive.c                      | 1853 ++++++++++++------------\n notes.c                                |    2 +-\n sha1-name.c                            |    2 +-\n t/t3401-rebase-and-am-rename.sh        |    8 +-\n t/t6043-merge-rename-directories.sh    |  462 +++++-\n t/t6046-merge-skip-unneeded-updates.sh |    8 +-\n tree-diff.c                            |    2 +-\n tree-walk.c                            |    6 +-\n tree-walk.h                            |    6 +-\n 19 files changed, 1367 insertions(+), 1025 deletions(-)\n\nRange-diff:\n 1:  bb5b410a61 =  1:  bb5b410a61 Use 'unsigned short' for mode, like diff_filespec does\n 2:  f91c28257e =  2:  f91c28257e merge-recursive: rename merge_options argument from 'o' to 'opt'\n 3:  e3fe8baa15 =  3:  e3fe8baa15 merge-recursive: rename diff_filespec 'one' to 'o'\n 4:  c6bd963ffb =  4:  c6bd963ffb merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'\n 5:  eca30e7571 =  5:  eca30e7571 merge-recursive: use 'ci' for rename_conflict_info variable name\n 6:  07f0d5fa8e =  6:  07f0d5fa8e merge-recursive: move some struct declarations together\n 7:  4cdd1ecbcb =  7:  4cdd1ecbcb merge-recursive: shrink rename_conflict_info\n 8:  3490324bdd =  8:  3490324bdd merge-recursive: remove ren[12]_other fields from rename_conflict_info\n 9:  fb73a2c55d =  9:  fb73a2c55d merge-recursive: track branch where rename occurred in rename struct\n10:  124ee08ed8 = 10:  124ee08ed8 merge-recursive: cleanup handle_rename_* function signatures\n11:  78a5916efe = 11:  78a5916efe merge-recursive: switch from (oid,mode) pairs to a diff_filespec\n12:  a8309326c1 = 12:  a8309326c1 t6043: fix copied test description to match its purpose\n13:  b362f4db1e = 13:  b362f4db1e merge-recursive: track information associated with directory renames\n14:  2e0258a358 = 14:  2e0258a358 merge-recursive: give callers of handle_content_merge() access to contents\n15:  719c25afaf ! 15:  428cdf62b3 merge-recursive: switch directory rename detection default\n    @@ -262,17 +262,12 @@\n      \t\tfree(value);\n      \t}\n     +\tif (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n    -+\t\tif (!strcasecmp(value, \"true\"))\n    -+\t\t\topt->detect_directory_renames = 2;\n    -+\t\telse if (!strcasecmp(value, \"false\"))\n    -+\t\t\topt->detect_directory_renames = 0;\n    -+\t\telse if (!strcasecmp(value, \"conflict\"))\n    ++\t\tint boolval = git_parse_maybe_bool(value);\n    ++\t\tif (0 <= boolval) {\n    ++\t\t\topt->detect_directory_renames = boolval ? 2 : 0;\n    ++\t\t} else if (!strcasecmp(value, \"conflict\")) {\n     +\t\t\topt->detect_directory_renames = 1;\n    -+\t\telse {\n    -+\t\t\terror(_(\"Invalid value for merge.directoryRenames: %s\"),\n    -+\t\t\t      value);\n    -+\t\t\topt->detect_directory_renames = 1;\n    -+\t\t}\n    ++\t\t} /* avoid erroring on values from future versions of git */\n     +\t\tfree(value);\n     +\t}\n      \tgit_config(git_xmerge_config, NULL);\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373206","messageId":"20190405150026.5260-10-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 09/15] merge-recursive: track branch where rename occurred in rename struct","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:20Z","receivedAt":"2019-04-05T15:01:03Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"We previously tracked the branch associated with a rename in a separate\nfield in rename_conflict_info, but since it is directly associated with\nthe rename it makes more sense to move it into the rename struct.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 113 +++++++++++++++++-----------------------------\n 1 file changed, 42 insertions(+), 71 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e66b47cfa1..f85c276f35 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -211,6 +211,7 @@ struct stage_data {\n \n struct rename {\n \tstruct diff_filepair *pair;\n+\tconst char *branch; /* branch that the rename occurred on */\n \t/*\n \t * Purpose of src_entry and dst_entry:\n \t *\n@@ -240,16 +241,12 @@ struct rename_conflict_info {\n \tenum rename_type rename_type;\n \tstruct rename *ren1;\n \tstruct rename *ren2;\n-\tconst char *branch1;\n-\tconst char *branch2;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct rename *ren1,\n-\t\t\t\t\t      struct rename *ren2,\n-\t\t\t\t\t      const char *branch1,\n-\t\t\t\t\t      const char *branch2)\n+\t\t\t\t\t      struct rename *ren2)\n {\n \tstruct rename_conflict_info *ci;\n \n@@ -260,11 +257,8 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (ren2 && branch1 != opt->branch1) {\n-\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t   opt,\n-\t\t\t\t\t   ren2,       ren1,\n-\t\t\t\t\t   branch2,    branch1);\n+\tif (ren2 && ren1->branch != opt->branch1) {\n+\t\tsetup_rename_conflict_info(rename_type, opt, ren2, ren1);\n \t\treturn;\n \t}\n \n@@ -272,8 +266,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tci->rename_type = rename_type;\n \tci->ren1 = ren1;\n \tci->ren2 = ren2;\n-\tci->branch1 = branch1;\n-\tci->branch2 = branch2;\n \n \tci->ren1->dst_entry->processed = 0;\n \tci->ren1->dst_entry->rename_conflict_info = ci;\n@@ -1665,12 +1657,15 @@ static int handle_rename_add(struct merge_options *opt,\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n \n-\tint other_stage = (ci->branch1 == opt->branch1 ? 3 : 2);\n+\tconst char *rename_branch = ci->ren1->branch;\n+\tconst char *add_branch = (opt->branch1 == rename_branch ?\n+\t\t\t\t  opt->branch2 : opt->branch1);\n+\tint other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2);\n \n \toutput(opt, 1, _(\"CONFLICT (rename/add): \"\n \t       \"Rename %s->%s in %s.  Added %s in %s\"),\n-\t       a->path, c->path, ci->branch1,\n-\t       c->path, ci->branch2);\n+\t       a->path, c->path, rename_branch,\n+\t       c->path, add_branch);\n \n \tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n \ttmp.path = a->path;\n@@ -1685,7 +1680,7 @@ static int handle_rename_add(struct merge_options *opt,\n \n \treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n-\t\t\t\t     ci->branch1, ci->branch2,\n+\t\t\t\t     rename_branch, add_branch,\n \t\t\t\t     &mfi.oid, mfi.mode,\n \t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n \t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n@@ -1727,14 +1722,14 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n-\t       o->path, a->path, ci->branch1,\n-\t       o->path, b->path, ci->branch2,\n+\t       o->path, a->path, ci->ren1->branch,\n+\t       o->path, b->path, ci->ren2->branch,\n \t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n \t\t\t    a->path, b->path, o->path);\n \tif (merge_mode_and_contents(opt, o, a, b, path_desc,\n-\t\t\t\t    ci->branch1, ci->branch2,\n+\t\t\t\t    ci->ren1->branch, ci->ren2->branch,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(path_desc);\n@@ -1781,14 +1776,15 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n-\t\t\t\t\t\t  ci->branch1, ci->branch2,\n+\t\t\t\t\t\t  ci->ren1->branch,\n+\t\t\t\t\t\t  ci->ren2->branch,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode,\n \t\t\t\t\t\t  &add->oid, add->mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n-\t\t\t\t\t\t\t\tci->branch1,\n-\t\t\t\t\t\t\t\tci->branch2);\n+\t\t\t\t\t\t\t\tci->ren1->branch,\n+\t\t\t\t\t\t\t\tci->ren2->branch);\n \t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n@@ -1800,14 +1796,15 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n-\t\t\t\t\t\t  ci->branch1, ci->branch2,\n+\t\t\t\t\t\t  ci->ren1->branch,\n+\t\t\t\t\t\t  ci->ren2->branch,\n \t\t\t\t\t\t  &add->oid, add->mode,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n-\t\t\t\t\t\t\t\tci->branch2,\n-\t\t\t\t\t\t\t\tci->branch1);\n+\t\t\t\t\t\t\t\tci->ren2->branch,\n+\t\t\t\t\t\t\t\tci->ren1->branch);\n \t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n@@ -1837,8 +1834,8 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n \t       \"Rename %s->%s in %s\"),\n-\t       a->path, c1->path, ci->branch1,\n-\t       b->path, c2->path, ci->branch2);\n+\t       a->path, c1->path, ci->ren1->branch,\n+\t       b->path, c2->path, ci->ren2->branch);\n \n \tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n \ttmp1.path = a->path;\n@@ -1858,7 +1855,7 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tfree(path_side_2_desc);\n \n \treturn handle_file_collision(opt, path, a->path, b->path,\n-\t\t\t\t     ci->branch1, ci->branch2,\n+\t\t\t\t     ci->ren1->branch, ci->ren2->branch,\n \t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n \t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n }\n@@ -2542,6 +2539,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n  * information; tree is always equal to either a_tree or b_tree.\n  */\n static struct string_list *get_renames(struct merge_options *opt,\n+\t\t\t\t       const char *branch,\n \t\t\t\t       struct diff_queue_struct *pairs,\n \t\t\t\t       struct hashmap *dir_renames,\n \t\t\t\t       struct hashmap *dir_rename_exclusions,\n@@ -2585,6 +2583,7 @@ static struct string_list *get_renames(struct merge_options *opt,\n \t\tre->processed = 0;\n \t\tre->add_turned_into_rename = 0;\n \t\tre->pair = pair;\n+\t\tre->branch = branch;\n \t\titem = string_list_lookup(entries, re->pair->one->path);\n \t\tif (!item)\n \t\t\tre->src_entry = insert_stage_data(re->pair->one->path,\n@@ -2639,7 +2638,6 @@ static int process_renames(struct merge_options *opt,\n \tfor (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {\n \t\tstruct string_list *renames1, *renames2Dst;\n \t\tstruct rename *ren1 = NULL, *ren2 = NULL;\n-\t\tconst char *branch1, *branch2;\n \t\tconst char *ren1_src, *ren1_dst;\n \t\tstruct string_list_item *lookup;\n \n@@ -2660,13 +2658,9 @@ static int process_renames(struct merge_options *opt,\n \t\tif (ren1) {\n \t\t\trenames1 = a_renames;\n \t\t\trenames2Dst = &b_by_dst;\n-\t\t\tbranch1 = opt->branch1;\n-\t\t\tbranch2 = opt->branch2;\n \t\t} else {\n \t\t\trenames1 = b_renames;\n \t\t\trenames2Dst = &a_by_dst;\n-\t\t\tbranch1 = opt->branch2;\n-\t\t\tbranch2 = opt->branch1;\n \t\t\tSWAP(ren2, ren1);\n \t\t}\n \n@@ -2706,12 +2700,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t     ren1->pair->two,\n \t\t\t\t\t     ren2->pair->two);\n \t\t\t}\n-\t\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t   ren2,\n-\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2);\n+\t\t\tsetup_rename_conflict_info(rename_type, opt, ren1, ren2);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n \t\t\t/* Two different files renamed to the same thing */\n \t\t\tchar *ren2_dst;\n@@ -2730,11 +2719,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\tren2->src_entry->processed = 1;\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n-\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t   ren2,\n-\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t   opt, ren1, ren2);\n \n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n@@ -2769,18 +2754,10 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n \t\t\t    ren1->add_turned_into_rename) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n \t\t\t\t   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {\n \t\t\t\t/*\n@@ -2807,11 +2784,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * file, then the merge will be clean.\n \t\t\t\t */\n \t\t\t\tsetup_rename_conflict_info(RENAME_ADD,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n@@ -2831,11 +2804,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t}\n \t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n-\t\t\t\t\t\t\t   opt,\n-\t\t\t\t\t\t\t   ren1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t}\n \t\t}\n \t}\n@@ -2904,13 +2873,13 @@ static int detect_and_process_renames(struct merge_options *opt,\n \t\tdir_rename_init(dir_re_merge);\n \t}\n \n-\tri->head_renames  = get_renames(opt, head_pairs,\n+\tri->head_renames  = get_renames(opt, opt->branch1, head_pairs,\n \t\t\t\t\tdir_re_merge, dir_re_head, head,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tri->merge_renames = get_renames(opt, merge_pairs,\n+\tri->merge_renames = get_renames(opt, opt->branch2, merge_pairs,\n \t\t\t\t\tdir_re_head, dir_re_merge, merge,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n@@ -3072,14 +3041,14 @@ static int handle_content_merge(struct merge_options *opt,\n \tif (ci) {\n \t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n \n-\t\tpath1 = (opt->branch1 == ci->branch1) ?\n+\t\tpath1 = (opt->branch1 == ci->ren1->branch) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\t/* If ci->ren2->pair != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n \t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n-\t\t\t opt->branch2 == ci->branch1) ?\n+\t\t\t opt->branch2 == ci->ren1->branch) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n@@ -3157,7 +3126,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(opt, path, ci->branch1);\n+\t\tnew_path = unique_path(opt, path, ci->ren1->branch);\n \t\tif (is_dirty) {\n \t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n@@ -3215,7 +3184,8 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair, ci->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair,\n+\t\t\t\t\t\t  ci->ren1->branch))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3230,7 +3200,8 @@ static int process_entry(struct merge_options *opt,\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n \t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n-\t\t\t\t\t\t ci->branch1, ci->branch2))\n+\t\t\t\t\t\t ci->ren1->branch,\n+\t\t\t\t\t\t ci->ren1->branch == opt->branch1 ? opt->branch2 : opt->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373207","messageId":"20190405150026.5260-7-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 06/15] merge-recursive: move some struct declarations together","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:17Z","receivedAt":"2019-04-05T15:01:03Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"These structs are related and reference each other, so move them\ntogether to make it easier for folks to determine what they hold and\nwhat their purpose is.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 78 +++++++++++++++++++++++------------------------\n 1 file changed, 39 insertions(+), 39 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ea5646debd..c4a2ef2a37 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -196,18 +196,6 @@ enum rename_type {\n \tRENAME_TWO_FILES_TO_ONE\n };\n \n-struct rename_conflict_info {\n-\tenum rename_type rename_type;\n-\tstruct diff_filepair *pair1;\n-\tstruct diff_filepair *pair2;\n-\tconst char *branch1;\n-\tconst char *branch2;\n-\tstruct stage_data *dst_entry1;\n-\tstruct stage_data *dst_entry2;\n-\tstruct diff_filespec ren1_other;\n-\tstruct diff_filespec ren2_other;\n-};\n-\n /*\n  * Since we want to write the index eventually, we cannot reuse the index\n  * for these (temporary) data.\n@@ -221,6 +209,45 @@ struct stage_data {\n \tunsigned processed:1;\n };\n \n+struct rename {\n+\tstruct diff_filepair *pair;\n+\t/*\n+\t * Purpose of src_entry and dst_entry:\n+\t *\n+\t * If 'before' is renamed to 'after' then src_entry will contain\n+\t * the versions of 'before' from the merge_base, HEAD, and MERGE in\n+\t * stages 1, 2, and 3; dst_entry will contain the respective\n+\t * versions of 'after' in corresponding locations.  Thus, we have a\n+\t * total of six modes and oids, though some will be null.  (Stage 0\n+\t * is ignored; we're interested in handling conflicts.)\n+\t *\n+\t * Since we don't turn on break-rewrites by default, neither\n+\t * src_entry nor dst_entry can have all three of their stages have\n+\t * non-null oids, meaning at most four of the six will be non-null.\n+\t * Also, since this is a rename, both src_entry and dst_entry will\n+\t * have at least one non-null oid, meaning at least two will be\n+\t * non-null.  Of the six oids, a typical rename will have three be\n+\t * non-null.  Only two implies a rename/delete, and four implies a\n+\t * rename/add.\n+\t */\n+\tstruct stage_data *src_entry;\n+\tstruct stage_data *dst_entry;\n+\tunsigned add_turned_into_rename:1;\n+\tunsigned processed:1;\n+};\n+\n+struct rename_conflict_info {\n+\tenum rename_type rename_type;\n+\tstruct diff_filepair *pair1;\n+\tstruct diff_filepair *pair2;\n+\tconst char *branch1;\n+\tconst char *branch2;\n+\tstruct stage_data *dst_entry1;\n+\tstruct stage_data *dst_entry2;\n+\tstruct diff_filespec ren1_other;\n+\tstruct diff_filespec ren2_other;\n+};\n+\n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct diff_filepair *pair1,\n \t\t\t\t\t      struct diff_filepair *pair2,\n@@ -645,33 +672,6 @@ static void record_df_conflict_files(struct merge_options *opt,\n \tstring_list_clear(&df_sorted_entries, 0);\n }\n \n-struct rename {\n-\tstruct diff_filepair *pair;\n-\t/*\n-\t * Purpose of src_entry and dst_entry:\n-\t *\n-\t * If 'before' is renamed to 'after' then src_entry will contain\n-\t * the versions of 'before' from the merge_base, HEAD, and MERGE in\n-\t * stages 1, 2, and 3; dst_entry will contain the respective\n-\t * versions of 'after' in corresponding locations.  Thus, we have a\n-\t * total of six modes and oids, though some will be null.  (Stage 0\n-\t * is ignored; we're interested in handling conflicts.)\n-\t *\n-\t * Since we don't turn on break-rewrites by default, neither\n-\t * src_entry nor dst_entry can have all three of their stages have\n-\t * non-null oids, meaning at most four of the six will be non-null.\n-\t * Also, since this is a rename, both src_entry and dst_entry will\n-\t * have at least one non-null oid, meaning at least two will be\n-\t * non-null.  Of the six oids, a typical rename will have three be\n-\t * non-null.  Only two implies a rename/delete, and four implies a\n-\t * rename/add.\n-\t */\n-\tstruct stage_data *src_entry;\n-\tstruct stage_data *dst_entry;\n-\tunsigned add_turned_into_rename:1;\n-\tunsigned processed:1;\n-};\n-\n static int update_stages(struct merge_options *opt, const char *path,\n \t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373208","messageId":"20190405150026.5260-8-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 07/15] merge-recursive: shrink rename_conflict_info","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:18Z","receivedAt":"2019-04-05T15:01:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The rename_conflict_info struct used both a pair and a stage_data which\nwere taken from a rename struct.  Just use the original rename struct.\nThis will also allow us to start making other simplifications to the\ncode.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 120 +++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 70 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c4a2ef2a37..e05f8f22f5 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -238,24 +238,20 @@ struct rename {\n \n struct rename_conflict_info {\n \tenum rename_type rename_type;\n-\tstruct diff_filepair *pair1;\n-\tstruct diff_filepair *pair2;\n+\tstruct rename *ren1;\n+\tstruct rename *ren2;\n \tconst char *branch1;\n \tconst char *branch2;\n-\tstruct stage_data *dst_entry1;\n-\tstruct stage_data *dst_entry2;\n \tstruct diff_filespec ren1_other;\n \tstruct diff_filespec ren2_other;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n-\t\t\t\t\t      struct diff_filepair *pair1,\n-\t\t\t\t\t      struct diff_filepair *pair2,\n+\t\t\t\t\t      struct merge_options *opt,\n+\t\t\t\t\t      struct rename *ren1,\n+\t\t\t\t\t      struct rename *ren2,\n \t\t\t\t\t      const char *branch1,\n \t\t\t\t\t      const char *branch2,\n-\t\t\t\t\t      struct stage_data *dst_entry1,\n-\t\t\t\t\t      struct stage_data *dst_entry2,\n-\t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct stage_data *src_entry1,\n \t\t\t\t\t      struct stage_data *src_entry2)\n {\n@@ -269,31 +265,27 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (dst_entry2 && branch1 != opt->branch1) {\n+\tif (ren2 && branch1 != opt->branch1) {\n \t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t   pair2,      pair1,\n-\t\t\t\t\t   branch2,    branch1,\n-\t\t\t\t\t   dst_entry2, dst_entry1,\n \t\t\t\t\t   opt,\n+\t\t\t\t\t   ren2,       ren1,\n+\t\t\t\t\t   branch2,    branch1,\n \t\t\t\t\t   src_entry2, src_entry1);\n \t\treturn;\n \t}\n \n \tci = xcalloc(1, sizeof(struct rename_conflict_info));\n \tci->rename_type = rename_type;\n-\tci->pair1 = pair1;\n+\tci->ren1 = ren1;\n+\tci->ren2 = ren2;\n \tci->branch1 = branch1;\n \tci->branch2 = branch2;\n \n-\tci->dst_entry1 = dst_entry1;\n-\tdst_entry1->rename_conflict_info = ci;\n-\tdst_entry1->processed = 0;\n+\tci->ren1->dst_entry->processed = 0;\n+\tci->ren1->dst_entry->rename_conflict_info = ci;\n \n-\tassert(!pair2 == !dst_entry2);\n-\tif (dst_entry2) {\n-\t\tci->dst_entry2 = dst_entry2;\n-\t\tci->pair2 = pair2;\n-\t\tdst_entry2->rename_conflict_info = ci;\n+\tif (ren2) {\n+\t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n \n \t/*\n@@ -305,7 +297,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n \t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n \n-\t\tci->ren1_other.path = pair1->one->path;\n+\t\tci->ren1_other.path = ren1->pair->one->path;\n \t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n \t\tci->ren1_other.mode = src_entry1->stages[ostage1].mode;\n \t}\n@@ -313,7 +305,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tif (rename_type == RENAME_TWO_FILES_TO_ONE) {\n \t\tostage2 = ostage1 ^ 1;\n \n-\t\tci->ren2_other.path = pair2->one->path;\n+\t\tci->ren2_other.path = ren2->pair->one->path;\n \t\toidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);\n \t\tci->ren2_other.mode = src_entry2->stages[ostage2].mode;\n \t}\n@@ -1694,8 +1686,8 @@ static int handle_rename_add(struct merge_options *opt,\n \t\t\t     struct rename_conflict_info *ci)\n {\n \t/* a was renamed to c, and a separate c was added. */\n-\tstruct diff_filespec *a = ci->pair1->one;\n-\tstruct diff_filespec *c = ci->pair1->two;\n+\tstruct diff_filespec *a = ci->ren1->pair->one;\n+\tstruct diff_filespec *c = ci->ren1->pair->two;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1718,8 +1710,8 @@ static int handle_rename_add(struct merge_options *opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi.oid, mfi.mode,\n-\t\t\t\t     &ci->dst_entry1->stages[other_stage].oid,\n-\t\t\t\t     ci->dst_entry1->stages[other_stage].mode);\n+\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n+\t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n }\n \n static char *find_path_for_conflict(struct merge_options *opt,\n@@ -1750,9 +1742,9 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \tstruct merge_file_info mfi;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n-\tstruct diff_filespec *o = ci->pair1->one;\n-\tstruct diff_filespec *a = ci->pair1->two;\n-\tstruct diff_filespec *b = ci->pair2->two;\n+\tstruct diff_filespec *o = ci->ren1->pair->one;\n+\tstruct diff_filespec *a = ci->ren1->pair->two;\n+\tstruct diff_filespec *b = ci->ren2->pair->two;\n \tchar *path_desc;\n \n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n@@ -1788,14 +1780,14 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * such cases, we should keep the added file around,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n \t\tif (add) {\n \t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n \t\t\tremove_file_from_index(opt->repo->index, a->path);\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n \t\tif (add) {\n \t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n \t\t\t\treturn -1;\n@@ -1808,7 +1800,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * rename/add collision.  If not, we can write the file out\n \t\t * to the specified location.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n@@ -1827,7 +1819,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n+\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n \t\tif (add) {\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n@@ -1854,10 +1846,10 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n-\tstruct diff_filespec *a = ci->pair1->one;\n-\tstruct diff_filespec *b = ci->pair2->one;\n-\tstruct diff_filespec *c1 = ci->pair1->two;\n-\tstruct diff_filespec *c2 = ci->pair2->two;\n+\tstruct diff_filespec *a = ci->ren1->pair->one;\n+\tstruct diff_filespec *b = ci->ren2->pair->one;\n+\tstruct diff_filespec *c1 = ci->ren1->pair->two;\n+\tstruct diff_filespec *c2 = ci->ren2->pair->two;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n@@ -2732,13 +2724,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t     ren2->pair->two);\n \t\t\t}\n \t\t\tsetup_rename_conflict_info(rename_type,\n-\t\t\t\t\t\t   ren1->pair,\n-\t\t\t\t\t\t   ren2->pair,\n+\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t   ren1,\n+\t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t   NULL);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n@@ -2759,13 +2749,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\tren2->src_entry->processed = 1;\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n-\t\t\t\t\t\t   ren1->pair,\n-\t\t\t\t\t\t   ren2->pair,\n+\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t   ren1,\n+\t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t   ren2->src_entry);\n \n@@ -2802,24 +2790,20 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n \t\t\t    ren1->add_turned_into_rename) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n@@ -2848,13 +2832,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * file, then the merge will be clean.\n \t\t\t\t */\n \t\t\t\tsetup_rename_conflict_info(RENAME_ADD,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else\n@@ -2876,13 +2858,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t}\n \t\t\t\tupdate_entry(ren1->dst_entry, o, a, b);\n \t\t\t\tsetup_rename_conflict_info(RENAME_NORMAL,\n-\t\t\t\t\t\t\t   ren1->pair,\n+\t\t\t\t\t\t\t   opt,\n+\t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   ren1->dst_entry,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n@@ -3119,15 +3099,15 @@ static int handle_content_merge(struct merge_options *opt,\n \tb.mode = b_mode;\n \n \tif (ci) {\n-\t\tstruct diff_filepair *pair1 = ci->pair1;\n+\t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n \n \t\tpath1 = (opt->branch1 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If ci->pair2 != NULL, we are in\n+\t\t/* If ci->ren2->pair != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n-\t\tpath2 = (ci->pair2 ||\n+\t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n \t\t\t opt->branch2 == ci->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n@@ -3264,7 +3244,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->pair1, ci->branch1))\n+\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair, ci->branch1))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3278,7 +3258,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt, ci->pair1,\n+\t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n \t\t\t\t\t\t ci->branch1, ci->branch2))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373209","messageId":"20190405150026.5260-9-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 08/15] merge-recursive: remove ren[12]_other fields from rename_conflict_info","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:19Z","receivedAt":"2019-04-05T15:01:06Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The ren1_other and ren2_other fields were synthesized from information\nin ren1->src_entry and ren2->src_entry.  Since we already have the\nnecessary information in ren1 and ren2, just use those.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 71 ++++++++++++++---------------------------------\n 1 file changed, 21 insertions(+), 50 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e05f8f22f5..e66b47cfa1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -242,8 +242,6 @@ struct rename_conflict_info {\n \tstruct rename *ren2;\n \tconst char *branch1;\n \tconst char *branch2;\n-\tstruct diff_filespec ren1_other;\n-\tstruct diff_filespec ren2_other;\n };\n \n static inline void setup_rename_conflict_info(enum rename_type rename_type,\n@@ -251,11 +249,8 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      struct rename *ren1,\n \t\t\t\t\t      struct rename *ren2,\n \t\t\t\t\t      const char *branch1,\n-\t\t\t\t\t      const char *branch2,\n-\t\t\t\t\t      struct stage_data *src_entry1,\n-\t\t\t\t\t      struct stage_data *src_entry2)\n+\t\t\t\t\t      const char *branch2)\n {\n-\tint ostage1 = 0, ostage2;\n \tstruct rename_conflict_info *ci;\n \n \t/*\n@@ -269,8 +264,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\tsetup_rename_conflict_info(rename_type,\n \t\t\t\t\t   opt,\n \t\t\t\t\t   ren2,       ren1,\n-\t\t\t\t\t   branch2,    branch1,\n-\t\t\t\t\t   src_entry2, src_entry1);\n+\t\t\t\t\t   branch2,    branch1);\n \t\treturn;\n \t}\n \n@@ -287,28 +281,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \tif (ren2) {\n \t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n-\n-\t/*\n-\t * For each rename, there could have been\n-\t * modifications on the side of history where that\n-\t * file was not renamed.\n-\t */\n-\tif (rename_type == RENAME_ADD ||\n-\t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n-\n-\t\tci->ren1_other.path = ren1->pair->one->path;\n-\t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n-\t\tci->ren1_other.mode = src_entry1->stages[ostage1].mode;\n-\t}\n-\n-\tif (rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage2 = ostage1 ^ 1;\n-\n-\t\tci->ren2_other.path = ren2->pair->one->path;\n-\t\toidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);\n-\t\tci->ren2_other.mode = src_entry2->stages[ostage2].mode;\n-\t}\n }\n \n static int show(struct merge_options *opt, int v)\n@@ -1688,6 +1660,7 @@ static int handle_rename_add(struct merge_options *opt,\n \t/* a was renamed to c, and a separate c was added. */\n \tstruct diff_filespec *a = ci->ren1->pair->one;\n \tstruct diff_filespec *c = ci->ren1->pair->two;\n+\tstruct diff_filespec tmp;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1699,8 +1672,12 @@ static int handle_rename_add(struct merge_options *opt,\n \t       a->path, c->path, ci->branch1,\n \t       c->path, ci->branch2);\n \n+\tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n+\ttmp.path = a->path;\n+\n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(opt, a, c, &ci->ren1_other, prev_path_desc,\n+\tif (merge_mode_and_contents(opt, a, c, &tmp,\n+\t\t\t\t    prev_path_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -1850,6 +1827,7 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tstruct diff_filespec *b = ci->ren2->pair->one;\n \tstruct diff_filespec *c1 = ci->ren1->pair->two;\n \tstruct diff_filespec *c2 = ci->ren2->pair->two;\n+\tstruct diff_filespec tmp1, tmp2;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n@@ -1862,12 +1840,17 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t       a->path, c1->path, ci->branch1,\n \t       b->path, c2->path, ci->branch2);\n \n+\tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n+\ttmp1.path = a->path;\n+\tfilespec_from_entry(&tmp2, ci->ren2->src_entry, 2);\n+\ttmp2.path = b->path;\n+\n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(opt, a, c1, &ci->ren1_other, path_side_1_desc,\n+\tif (merge_mode_and_contents(opt, a, c1, &tmp1, path_side_1_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(opt, b, &ci->ren2_other, c2, path_side_2_desc,\n+\t    merge_mode_and_contents(opt, b, &tmp2, c2, path_side_2_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n@@ -2728,9 +2711,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t   branch2);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n \t\t\t/* Two different files renamed to the same thing */\n \t\t\tchar *ren2_dst;\n@@ -2753,9 +2734,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t   ren2,\n \t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t   ren1->src_entry,\n-\t\t\t\t\t\t   ren2->src_entry);\n+\t\t\t\t\t\t   branch2);\n \n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n@@ -2794,18 +2773,14 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n \t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n \t\t\t\t   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {\n \t\t\t\t/*\n@@ -2836,9 +2811,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   branch2,\n-\t\t\t\t\t\t\t   ren1->src_entry,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\t\t\t\t\t   branch2);\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n@@ -2862,8 +2835,6 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t\t   ren1,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   branch1,\n-\t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n \t\t}\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373210","messageId":"20190405150026.5260-5-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 04/15] merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:15Z","receivedAt":"2019-04-05T15:01:07Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Since we want to replace oid,mode pairs with a single diff_filespec,\nwe will soon want to be able to use the names 'o', 'a', and 'b' for\nthe three different file versions.  Rename some local variables in\nblob_unchanged() that would otherwise conflict.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 18 ++++++++++--------\n 1 file changed, 10 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 36af5d9cc6..4ed1b48630 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -3031,9 +3031,10 @@ static int blob_unchanged(struct merge_options *opt,\n \t\t\t  unsigned a_mode,\n \t\t\t  int renormalize, const char *path)\n {\n-\tstruct strbuf o = STRBUF_INIT;\n-\tstruct strbuf a = STRBUF_INIT;\n+\tstruct strbuf obuf = STRBUF_INIT;\n+\tstruct strbuf abuf = STRBUF_INIT;\n \tint ret = 0; /* assume changed for safety */\n+\tconst struct index_state *idx = opt->repo->index;\n \n \tif (a_mode != o_mode)\n \t\treturn 0;\n@@ -3043,20 +3044,21 @@ static int blob_unchanged(struct merge_options *opt,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &obuf) ||\n+\t    read_oid_strbuf(opt, a_oid, &abuf))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n \t * performed.  Comparison can be skipped if both files are\n \t * unchanged since their sha1s have already been compared.\n \t */\n-\tif (renormalize_buffer(opt->repo->index, path, o.buf, o.len, &o) |\n-\t    renormalize_buffer(opt->repo->index, path, a.buf, a.len, &a))\n-\t\tret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));\n+\tif (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) |\n+\t    renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf))\n+\t\tret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len));\n \n error_return:\n-\tstrbuf_release(&o);\n-\tstrbuf_release(&a);\n+\tstrbuf_release(&obuf);\n+\tstrbuf_release(&abuf);\n \treturn ret;\n }\n \n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373212","messageId":"20190405150026.5260-16-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 15/15] merge-recursive: switch directory rename detection default","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:26Z","receivedAt":"2019-04-05T15:01:10Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"When all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one\nbranch, there is a question about whether x/d added on a different\nbranch should remain at x/d or appear at z/d when the two branches are\nmerged.  There are different possible viewpoints here:\n\n  A) The file was placed at x/d; it's unrelated to the other files in\n     x/ so it doesn't matter that all the files from x/ moved to z/ on\n     one branch; x/d should still remain at x/d.\n\n  B) x/d is related to the other files in x/, and x/ was renamed to z/;\n     therefore x/d should be moved to z/d.\n\nSince there was no ability to detect directory renames prior to\ngit-2.18, users experienced (A) regardless of context.  Choice (B) was\nimplemented in git-2.18, with no option to go back to (A), and has been\nin use since.  However, one user reported that the merge results did not\nmatch their expectations, making the change of default problematic,\nespecially since there was no notice printed when directory rename\ndetection moved files.\n\nNote that there is also a third possibility here:\n\n  C) There are different answers depending on the context and content\n     that cannot be determined by git, so this is a conflict.  Use a\n     higher stage in the index to record the conflict and notify the\n     user of the potential issue instead of silently selecting a\n     resolution for them.\n\nAdd an option for users to specify their preference for whether to use\ndirectory rename detection, and default to (C).  Even when directory\nrename detection is on, add notice messages about files moved into new\ndirectories.\n\nAs a sidenote, x/d did not have to be a new file here; it could have\nalready existed at some other path and been renamed to x/d, with\ndirectory rename detection just renaming it again to z/d.  Thus, it's\nnot just new files, but also a modification to all rename types (normal\nrenames, rename/add, rename/delete, rename/rename(1to1),\nrename/rename(1to2), and rename/rename(2to1)).\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt         |  19 +-\n merge-recursive.c                      | 146 ++++++--\n t/t3401-rebase-and-am-rename.sh        |   8 +-\n t/t6043-merge-rename-directories.sh    | 458 ++++++++++++++++++++++---\n t/t6046-merge-skip-unneeded-updates.sh |   8 +-\n 5 files changed, 552 insertions(+), 87 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex d389c73929..6a313937f8 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -39,9 +39,22 @@ merge.renameLimit::\n \tis turned off.\n \n merge.renames::\n-\tWhether and how Git detects renames.  If set to \"false\",\n-\trename detection is disabled. If set to \"true\", basic rename\n-\tdetection is enabled.  Defaults to the value of diff.renames.\n+\tWhether Git detects renames.  If set to \"false\", rename detection\n+\tis disabled. If set to \"true\", basic rename detection is enabled.\n+\tDefaults to the value of diff.renames.\n+\n+merge.directoryRenames::\n+\tWhether Git detects directory renames, affecting what happens at\n+\tmerge time to new files added to a directory on one side of\n+\thistory when that directory was renamed on the other side of\n+\thistory.  If merge.directoryRenames is set to \"false\", directory\n+\trename detection is disabled, meaning that such new files will be\n+\tleft behind in the old directory.  If set to \"true\", directory\n+\trename detection is enabled, meaning that such new files will be\n+\tmoved into the new directory.  If set to \"conflict\", a conflict\n+\twill be reported for such paths.  If merge.renames is false,\n+\tmerge.directoryRenames is ignored and treated as false.  Defaults\n+\tto \"conflict\".\n \n merge.renormalize::\n \tTell Git that canonical representation of files in the\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2edfa01e43..36af441faa 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1370,30 +1370,39 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t */\n \tconst struct rename *ren = ci->ren1;\n \tconst struct diff_filespec *dest = ren->pair->two;\n+\tchar *file_path = dest->path;\n+\tint mark_conflicted = (opt->detect_directory_renames == 1);\n+\tassert(ren->dir_rename_original_dest);\n \n \tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n-\t\tchar *alt_path = unique_path(opt, dest->path, ren->branch);\n-\n+\t\tmark_conflicted = 1;\n+\t\tfile_path = unique_path(opt, dest->path, ren->branch);\n \t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n-\t\t\t       \"writing to %s instead.\"),\n-\t\t       dest->path, alt_path);\n+\t\t\t\t \"writing to %s instead.\"),\n+\t\t       dest->path, file_path);\n+\t}\n+\n+\tif (mark_conflicted) {\n \t\t/*\n-\t\t * Write the file in worktree at alt_path, but not in the\n-\t\t * index.  Instead, write to dest->path for the index but\n-\t\t * only at the higher appropriate stage.\n+\t\t * Write the file in worktree at file_path.  In the index,\n+\t\t * only record the file at dest->path in the appropriate\n+\t\t * higher stage.\n \t\t */\n-\t\tif (update_file(opt, 0, dest, alt_path))\n+\t\tif (update_file(opt, 0, dest, file_path))\n \t\t\treturn -1;\n-\t\tfree(alt_path);\n-\t\treturn update_stages(opt, dest->path, NULL,\n-\t\t\t\t     ren->branch == opt->branch1 ? dest : NULL,\n-\t\t\t\t     ren->branch == opt->branch1 ? NULL : dest);\n+\t\tif (file_path != dest->path)\n+\t\t\tfree(file_path);\n+\t\tif (update_stages(opt, dest->path, NULL,\n+\t\t\t\t  ren->branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t  ren->branch == opt->branch1 ? NULL : dest))\n+\t\t\treturn -1;\n+\t\treturn 0; /* not clean, but conflicted */\n+\t} else {\n+\t\t/* Update dest->path both in index and in worktree */\n+\t\tif (update_file(opt, 1, dest, dest->path))\n+\t\t\treturn -1;\n+\t\treturn 1; /* clean */\n \t}\n-\n-\t/* Update dest->path both in index and in worktree */\n-\tif (update_file(opt, 1, dest, dest->path))\n-\t\treturn -1;\n-\treturn 0;\n }\n \n static int handle_change_delete(struct merge_options *opt,\n@@ -3090,10 +3099,88 @@ static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n-\t/* Merge the content and write it out */\n+\tstruct rename *ren = ci->ren1;\n \tstruct merge_file_info mfi;\n-\treturn handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n-\t\t\t\t    o, a, b, ci);\n+\tint clean;\n+\tint side = (ren->branch == opt->branch1 ? 2 : 3);\n+\n+\t/* Merge the content and write it out */\n+\tclean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n+\t\t\t\t     o, a, b, ci);\n+\n+\tif (clean && opt->detect_directory_renames == 1 &&\n+\t    ren->dir_rename_original_dest) {\n+\t\tif (update_stages(opt, path,\n+\t\t\t\t  NULL,\n+\t\t\t\t  side == 2 ? &mfi.blob : NULL,\n+\t\t\t\t  side == 2 ? NULL : &mfi.blob))\n+\t\t\treturn -1;\n+\t\tclean = 0; /* not clean, but conflicted */\n+\t}\n+\treturn clean;\n+}\n+\n+static void dir_rename_warning(const char *msg,\n+\t\t\t       int is_add,\n+\t\t\t       int clean,\n+\t\t\t       struct merge_options *opt,\n+\t\t\t       struct rename *ren)\n+{\n+\tconst char *other_branch;\n+\tother_branch = (ren->branch == opt->branch1 ?\n+\t\t\topt->branch2 : opt->branch1);\n+\tif (is_add) {\n+\t\toutput(opt, clean ? 2 : 1, msg,\n+\t\t       ren->pair->one->path, ren->branch,\n+\t\t       other_branch, ren->pair->two->path);\n+\t\treturn;\n+\t}\n+\toutput(opt, clean ? 2 : 1, msg,\n+\t       ren->pair->one->path, ren->dir_rename_original_dest, ren->branch,\n+\t       other_branch, ren->pair->two->path);\n+}\n+static int warn_about_dir_renamed_entries(struct merge_options *opt,\n+\t\t\t\t\t  struct rename *ren)\n+{\n+\tconst char *msg;\n+\tint clean = 1, is_add;\n+\n+\tif (!ren)\n+\t\treturn clean;\n+\n+\t/* Return early if ren was not affected/created by a directory rename */\n+\tif (!ren->dir_rename_original_dest)\n+\t\treturn clean;\n+\n+\t/* Sanity checks */\n+\tassert(opt->detect_directory_renames > 0);\n+\tassert(ren->dir_rename_original_type == 'A' ||\n+\t       ren->dir_rename_original_type == 'R');\n+\n+\t/* Check whether to treat directory renames as a conflict */\n+\tclean = (opt->detect_directory_renames == 2);\n+\n+\tis_add = (ren->dir_rename_original_type == 'A');\n+\tif (ren->dir_rename_original_type == 'A' && clean) {\n+\t\tmsg = _(\"Path updated: %s added in %s inside a \"\n+\t\t\t\"directory that was renamed in %s; moving it to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'A' && !clean) {\n+\t\tmsg = _(\"CONFLICT (file location): %s added in %s \"\n+\t\t\t\"inside a directory that was renamed in %s, \"\n+\t\t\t\"suggesting it should perhaps be moved to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'R' && clean) {\n+\t\tmsg = _(\"Path updated: %s renamed to %s in %s, inside a \"\n+\t\t\t\"directory that was renamed in %s; moving it to %s.\");\n+\t} else if (ren->dir_rename_original_type == 'R' && !clean) {\n+\t\tmsg = _(\"CONFLICT (file location): %s renamed to %s in %s, \"\n+\t\t\t\"inside a directory that was renamed in %s, \"\n+\t\t\t\"suggesting it should perhaps be moved to %s.\");\n+\t} else {\n+\t\tBUG(\"Impossible dir_rename_original_type/clean combination\");\n+\t}\n+\tdir_rename_warning(msg, is_add, clean, opt, ren);\n+\n+\treturn clean;\n }\n \n /* Per entry merge function */\n@@ -3115,6 +3202,10 @@ static int process_entry(struct merge_options *opt,\n \tif (entry->rename_conflict_info) {\n \t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n \t\tstruct diff_filespec *temp;\n+\t\tint path_clean;\n+\n+\t\tpath_clean = warn_about_dir_renamed_entries(opt, ci->ren1);\n+\t\tpath_clean &= warn_about_dir_renamed_entries(opt, ci->ren2);\n \n \t\t/*\n \t\t * For cases with a single rename, {o,a,b}->path have all been\n@@ -3135,9 +3226,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n-\t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci))\n-\t\t\t\tclean_merge = -1;\n+\t\t\tclean_merge = handle_rename_via_dir(opt, ci);\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n \t\t\t/*\n@@ -3187,6 +3276,8 @@ static int process_entry(struct merge_options *opt,\n \t\t\tentry->processed = 0;\n \t\t\tbreak;\n \t\t}\n+\t\tif (path_clean < clean_merge)\n+\t\t\tclean_merge = path_clean;\n \t} else if (o_valid && (!a_valid || !b_valid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_valid && !b_valid) ||\n@@ -3558,6 +3649,15 @@ static void merge_recursive_config(struct merge_options *opt)\n \t\topt->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n \t\tfree(value);\n \t}\n+\tif (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n+\t\tint boolval = git_parse_maybe_bool(value);\n+\t\tif (0 <= boolval) {\n+\t\t\topt->detect_directory_renames = boolval ? 2 : 0;\n+\t\t} else if (!strcasecmp(value, \"conflict\")) {\n+\t\t\topt->detect_directory_renames = 1;\n+\t\t} /* avoid erroring on values from future versions of git */\n+\t\tfree(value);\n+\t}\n \tgit_config(git_xmerge_config, NULL);\n }\n \ndiff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh\nindex e0b5111993..a0b9438b22 100755\n--- a/t/t3401-rebase-and-am-rename.sh\n+++ b/t/t3401-rebase-and-am-rename.sh\n@@ -42,7 +42,7 @@ test_expect_success 'rebase --interactive: directory rename detected' '\n \t\tgit checkout B^0 &&\n \n \t\tset_fake_editor &&\n-\t\tFAKE_LINES=\"1\" git rebase --interactive A &&\n+\t\tFAKE_LINES=\"1\" git -c merge.directoryRenames=true rebase --interactive A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -58,7 +58,7 @@ test_expect_failure 'rebase (am): directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase A &&\n+\t\tgit -c merge.directoryRenames=true rebase A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -74,7 +74,7 @@ test_expect_success 'rebase --merge: directory rename detected' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tgit rebase --merge A &&\n+\t\tgit -c merge.directoryRenames=true rebase --merge A &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -92,7 +92,7 @@ test_expect_failure 'am: directory rename detected' '\n \n \t\tgit format-patch -1 B &&\n \n-\t\tgit am --3way 0001*.patch &&\n+\t\tgit -c merge.directoryRenames=true am --3way 0001*.patch &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\ndiff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh\nindex fe205be607..50b7543483 100755\n--- a/t/t6043-merge-rename-directories.sh\n+++ b/t/t6043-merge-rename-directories.sh\n@@ -75,7 +75,7 @@ test_expect_success '1a-check: Simple directory rename detection' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -142,7 +142,7 @@ test_expect_success '1b-check: Merge a directory with another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -201,7 +201,7 @@ test_expect_success '1c-check: Transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -270,7 +270,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -350,7 +350,7 @@ test_expect_success '1e-check: Renamed directory, with all files being renamed t\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -416,7 +416,7 @@ test_expect_success '1f-check: Split a directory into two other directories' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -497,7 +497,7 @@ test_expect_success '2a-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*directory rename split\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -559,7 +559,7 @@ test_expect_success '2b-check: Directory split into two on one side, with equal\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -640,7 +640,7 @@ test_expect_success '3a-check: Avoid implicit rename if involved as source on ot\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -705,7 +705,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&\n \t\ttest_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&\n \n@@ -826,7 +826,7 @@ test_expect_success '4a-check: Directory split, with original directory still pr\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -915,7 +915,7 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT.*implicit dir rename\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -989,7 +989,7 @@ test_expect_success '5b-check: Rename/delete in order to get add/add/add conflic\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1069,7 +1069,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*z/d\" out &&\n \t\ttest_i18ngrep \"CONFLICT (add/add).* y/d\" out &&\n \n@@ -1153,7 +1153,7 @@ test_expect_success '5d-check: Directory/file/file conflict due to directory ren\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (file/directory).*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1243,7 +1243,7 @@ test_expect_success '6a-check: Tricky rename/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*z/c.*y/c\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1308,7 +1308,7 @@ test_expect_success '6b-check: Same rename done on both sides' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1370,7 +1370,7 @@ test_expect_success '6c-check: Rename only done on same side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1432,7 +1432,7 @@ test_expect_success '6d-check: We do not always want transitive renaming' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -1495,7 +1495,7 @@ test_expect_success '6e-check: Add/add from one side' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -1591,7 +1591,7 @@ test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/b.*y/b.*w/b\" out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*z/c.*y/c.*x/c\" out &&\n \n@@ -1663,7 +1663,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1740,7 +1740,7 @@ test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename).*x/d.*w/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1804,7 +1804,7 @@ test_expect_success '7d-check: transitive rename involved in rename/delete; how\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1894,7 +1894,7 @@ test_expect_success '7e-check: transitive rename in rename/delete AND dirs in th\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*x/d.*y/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -1985,7 +1985,7 @@ test_expect_success '8a-check: Dual-directory rename, one into the others way' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2063,7 +2063,7 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -2135,7 +2135,7 @@ test_expect_success '8c-check: modify/delete or rename+modify/delete' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"CONFLICT (modify/delete).* z/d\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2212,7 +2212,7 @@ test_expect_success '8d-check: rename/delete...or not?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2287,7 +2287,7 @@ test_expect_success '8e-check: Both sides rename, one side adds to original dire\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&\n \n@@ -2374,7 +2374,7 @@ test_expect_success '9a-check: Inner renamed directory within outer renamed dire\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 7 out &&\n@@ -2444,7 +2444,7 @@ test_expect_success '9b-check: Transitive rename with content merge' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2534,7 +2534,7 @@ test_expect_success '9c-check: Doubly transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> z rename to x/f\" out &&\n \n \t\tgit ls-files -s >out &&\n@@ -2622,7 +2622,7 @@ test_expect_success '9d-check: N-way transitive rename?' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 >out &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying z -> y rename to z/t\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying y -> x rename to y/a\" out &&\n \t\ttest_i18ngrep \"WARNING: Avoiding applying x -> w rename to x/b\" out &&\n@@ -2704,7 +2704,7 @@ test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\tgrep \"CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo\" out >error_line &&\n \t\tgrep -q dir1/yo error_line &&\n \t\tgrep -q dir2/yo error_line &&\n@@ -2782,7 +2782,7 @@ test_expect_success '9f-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2849,7 +2849,7 @@ test_expect_failure '9g-check: Renamed directory that only contained immediate s\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -2918,7 +2918,7 @@ test_expect_success '9h-check: Avoid dir rename on merely modified path' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -2993,7 +2993,7 @@ test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '\n \t\techo very >z/c &&\n \t\techo important >z/d &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"The following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3061,7 +3061,7 @@ test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '\n \t\techo important >y/d &&\n \t\techo contents >y/e &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/delete).*Version B\\^0 of y/d left in tree at y/d~B\\^0\" out &&\n \t\ttest_i18ngrep \"Error: Refusing to lose untracked file at y/e; writing to y/e~B\\^0 instead\" out &&\n \n@@ -3137,7 +3137,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tgit checkout A^0 &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~B\\^0 instead\" out &&\n \n@@ -3174,7 +3174,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)\n \t\tmkdir y &&\n \t\techo important >y/c &&\n \n-\t\ttest_must_fail git merge -s recursive A^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/c; adding as y/c~HEAD instead\" out &&\n \n@@ -3249,7 +3249,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '\n \t\tgit checkout A^0 &&\n \t\techo important >y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose untracked file at y/wham\" out &&\n \n@@ -3327,7 +3327,7 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n\n \t\tmkdir z &&\n \t\techo random >z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep ! \"following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n@@ -3407,7 +3407,7 @@ test_expect_success '11a-check: Avoid losing dirty contents with simple rename'\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\ttest_seq 1 10 >expected &&\n@@ -3479,7 +3479,7 @@ test_expect_success '11b-check: Avoid losing dirty file involved in directory re\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\tgit merge -s recursive B^0 >out 2>err &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3554,7 +3554,7 @@ test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"following files would be overwritten by merge\" err &&\n \n \t\tgrep -q stuff y/c &&\n@@ -3621,7 +3621,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf\n \t\tgit checkout A^0 &&\n \t\techo stuff >>z/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at z/c\" out &&\n \n \t\tgrep -q stuff z/c &&\n@@ -3700,7 +3700,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo mods >>y/c &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/c\" out &&\n \n@@ -3782,7 +3782,7 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena\n \t\tgit checkout A^0 &&\n \t\techo important >>y/wham &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \t\ttest_i18ngrep \"CONFLICT (rename/rename)\" out &&\n \t\ttest_i18ngrep \"Refusing to lose dirty file at y/wham\" out &&\n \n@@ -3870,7 +3870,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 6 out &&\n@@ -3946,7 +3946,7 @@ test_expect_success '12b-check: Moving two directory hierarchies into each other\n \n \t\tgit checkout A^0 &&\n \n-\t\tgit merge -s recursive B^0 &&\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 4 out &&\n@@ -4016,7 +4016,7 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \n \t\tgit checkout A^0 &&\n \n-\t\ttest_must_fail git merge -s recursive B^0 &&\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&\n \n \t\tgit ls-files -u >out &&\n \t\ttest_line_count = 12 out &&\n@@ -4051,4 +4051,356 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c\n \t)\n '\n \n+###########################################################################\n+# SECTION 13: Checking informational and conflict messages\n+#\n+# A year after directory rename detection became the default, it was\n+# instead decided to report conflicts on the pathname on the basis that\n+# some users may expect the new files added or moved into a directory to\n+# be unrelated to all the other files in that directory, and thus that\n+# directory rename detection is unexpected.  Test that the messages printed\n+# match our expectation.\n+###########################################################################\n+\n+# Testcase 13a, Basic directory rename with newly added files\n+#   Commit O: z/{b,c}\n+#   Commit A: y/{b,c}\n+#   Commit B: z/{b,c,d,e/f}\n+#   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f\n+\n+test_expect_success '13a-setup: messages for newly added files' '\n+\ttest_create_repo 13a &&\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tmkdir z &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo d >z/d &&\n+\t\tmkdir z/e &&\n+\t\techo f >z/e/f &&\n+\t\tgit add z/d z/e/f &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13a-check(conflict): messages for newly added files' '\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/[de]\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d &&\n+\t\ttest_path_is_missing z/e/f &&\n+\t\ttest_path_is_file    y/e/f\n+\t)\n+'\n+\n+test_expect_success '13a-check(info): messages for newly added files' '\n+\t(\n+\t\tcd 13a &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&\n+\t\ttest_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/[de]\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d &&\n+\t\ttest_path_is_missing z/e/f &&\n+\t\ttest_path_is_file    y/e/f\n+\t)\n+'\n+\n+# Testcase 13b, Transitive rename with conflicted content merge and default\n+#               \"conflict\" setting\n+#   (Related to testcase 1c, 9b)\n+#   Commit O: z/{b,c},   x/d_1\n+#   Commit A: y/{b,c},   x/d_2\n+#   Commit B: z/{b,c,d_3}\n+#   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,\n+#             one about content, and one about file location\n+\n+test_expect_success '13b-setup: messages for transitive rename with conflicted content' '\n+\ttest_create_repo 13b &&\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tmkdir x &&\n+\t\tmkdir z &&\n+\t\ttest_seq 1 10 >x/d &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add x z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\techo 11 >>x/d &&\n+\t\tgit add x/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\techo eleven >>x/d &&\n+\t\tgit mv x/d z/d &&\n+\t\tgit add z/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '\n+\t(\n+\t\tcd 13b &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&\n+\t\ttest_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+# Testcase 13c, Rename/rename(1to1) due to directory rename\n+#   Commit O: z/{b,c},   x/{d,e}\n+#   Commit A: y/{b,c,d}, x/e\n+#   Commit B: z/{b,c,d}, x/e\n+#   Expected: y/{b,c,d}, with info or conflict messages for d (\n+#             A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d\n+#             One could argue A had partial knowledge of what was done with\n+#             d and B had full knowledge, but that's a slippery slope as\n+#             shown in testcase 13d.\n+\n+test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '\n+\ttest_create_repo 13c &&\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tmkdir x &&\n+\t\tmkdir z &&\n+\t\ttest_seq 1 10 >x/d &&\n+\t\techo e >x/e &&\n+\t\techo b >z/b &&\n+\t\techo c >z/c &&\n+\t\tgit add x z &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv z y &&\n+\t\tgit mv x/d y/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv x/d z/d &&\n+\t\tgit add z/d &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '\n+\t(\n+\t\tcd 13c &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep z/ paths &&\n+\t\tgrep \"y/d\" paths &&\n+\n+\t\ttest_path_is_missing z/d &&\n+\t\ttest_path_is_file    y/d\n+\t)\n+'\n+\n+# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides\n+#   Commit O: a/{z,y}, b/x,     c/w\n+#   Commit A: a/z,     b/{y,x}, d/w\n+#   Commit B: a/z,     d/x,     c/{y,w}\n+#   Expected: a/z, d/{y,x,w} with no file location conflict for x\n+#             Easy cases:\n+#               * z is always in a; so it stays in a.\n+#               * x starts in b, only modified on one side to move into d/\n+#               * w starts in c, only modified on one side to move into d/\n+#             Hard case:\n+#               * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y\n+#               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y\n+#               No conflict in where a/y ends up, so put it in d/y.\n+\n+test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '\n+\ttest_create_repo 13d &&\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tmkdir a &&\n+\t\tmkdir b &&\n+\t\tmkdir c &&\n+\t\techo z >a/z &&\n+\t\techo y >a/y &&\n+\t\techo x >b/x &&\n+\t\techo w >c/w &&\n+\t\tgit add a b c &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"O\" &&\n+\n+\t\tgit branch O &&\n+\t\tgit branch A &&\n+\t\tgit branch B &&\n+\n+\t\tgit checkout A &&\n+\t\tgit mv a/y b/ &&\n+\t\tgit mv c/ d/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"A\" &&\n+\n+\t\tgit checkout B &&\n+\t\tgit mv a/y c/ &&\n+\t\tgit mv b/ d/ &&\n+\t\ttest_tick &&\n+\t\tgit commit -m \"B\"\n+\t)\n+'\n+\n+test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tgit checkout A^0 &&\n+\n+\t\ttest_must_fail git merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&\n+\t\ttest_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep b/ paths &&\n+\t\t! grep c/ paths &&\n+\t\tgrep \"d/y\" paths &&\n+\n+\t\ttest_path_is_missing b/y &&\n+\t\ttest_path_is_missing c/y &&\n+\t\ttest_path_is_file    d/y\n+\t)\n+'\n+\n+test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '\n+\t(\n+\t\tcd 13d &&\n+\n+\t\tgit reset --hard &&\n+\t\tgit checkout A^0 &&\n+\n+\t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n+\n+\t\ttest_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&\n+\t\ttest_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&\n+\n+\t\tgit ls-files >paths &&\n+\t\t! grep b/ paths &&\n+\t\t! grep c/ paths &&\n+\t\tgrep \"d/y\" paths &&\n+\n+\t\ttest_path_is_missing b/y &&\n+\t\ttest_path_is_missing c/y &&\n+\t\ttest_path_is_file    d/y\n+\t)\n+'\n+\n test_done\ndiff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh\nindex 38e24f787c..3a47623ed3 100755\n--- a/t/t6046-merge-skip-unneeded-updates.sh\n+++ b/t/t6046-merge-skip-unneeded-updates.sh\n@@ -466,7 +466,7 @@ test_expect_success '3a-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -495,7 +495,7 @@ test_expect_success '3a-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -560,7 +560,7 @@ test_expect_success '3b-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout A^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n@@ -589,7 +589,7 @@ test_expect_success '3b-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '\n \n \t\tgit checkout B^0 &&\n \n-\t\tGIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&\n+\t\tGIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&\n \n \t\ttest_i18ngrep ! \"Skipped bar/bq\" out &&\n \t\ttest_must_be_empty err &&\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373211","messageId":"20190405150026.5260-15-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 14/15] merge-recursive: give callers of handle_content_merge() access to contents","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:25Z","receivedAt":"2019-04-05T15:01:11Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Pass a merge_file_info struct to handle_content_merge() so that the\ncallers can access the oid and mode of the result afterward.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 37 ++++++++++++++++++++-----------------\n 1 file changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 938a526b20..2edfa01e43 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2982,7 +2982,8 @@ static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n-static int handle_content_merge(struct merge_options *opt,\n+static int handle_content_merge(struct merge_file_info *mfi,\n+\t\t\t\tstruct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n \t\t\t\tconst struct diff_filespec *o,\n@@ -2991,7 +2992,6 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n-\tstruct merge_file_info mfi;\n \tunsigned df_conflict_remains = 0;\n \n \tif (!is_valid(o))\n@@ -3004,7 +3004,7 @@ static int handle_content_merge(struct merge_options *opt,\n \n \tif (merge_mode_and_contents(opt, o, a, b, path,\n \t\t\t\t    opt->branch1, opt->branch2,\n-\t\t\t\t    opt->call_depth * 2, &mfi))\n+\t\t\t\t    opt->call_depth * 2, mfi))\n \t\treturn -1;\n \n \t/*\n@@ -3013,13 +3013,13 @@ static int handle_content_merge(struct merge_options *opt,\n \t *   b) The merge matches what was in HEAD (content, mode, pathname)\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n-\tif (mfi.clean && was_tracked_and_matches(opt, path, &mfi.blob) &&\n+\tif (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n \t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(opt, &mfi.blob, path,\n+\t\tif (add_cacheinfo(opt, &mfi->blob, path,\n \t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n@@ -3035,11 +3035,11 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\tce = opt->repo->index->cache[pos];\n \t\t\tce->ce_flags |= CE_SKIP_WORKTREE;\n \t\t}\n-\t\treturn mfi.clean;\n+\t\treturn mfi->clean;\n \t}\n \n-\tif (!mfi.clean) {\n-\t\tif (S_ISGITLINK(mfi.blob.mode))\n+\tif (!mfi->clean) {\n+\t\tif (S_ISGITLINK(mfi->blob.mode))\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n@@ -3053,15 +3053,15 @@ static int handle_content_merge(struct merge_options *opt,\n \t\tif (opt->call_depth) {\n \t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n-\t\t\tif (!mfi.clean) {\n+\t\t\tif (!mfi->clean) {\n \t\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n \n \t\t\t\tif (update_stages(opt, path, NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? &mfi.blob : NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi.blob))\n+\t\t\t\t\t\t  file_from_stage2 ? &mfi->blob : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi->blob))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n@@ -3072,15 +3072,15 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t       path);\n \t\t}\n \t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(opt, 0, &mfi.blob, new_path)) {\n+\t\tif (update_file(opt, 0, &mfi->blob, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n-\t\tmfi.clean = 0;\n-\t} else if (update_file(opt, mfi.clean, &mfi.blob, path))\n+\t\tmfi->clean = 0;\n+\t} else if (update_file(opt, mfi->clean, &mfi->blob, path))\n \t\treturn -1;\n-\treturn !is_dirty && mfi.clean;\n+\treturn !is_dirty && mfi->clean;\n }\n \n static int handle_rename_normal(struct merge_options *opt,\n@@ -3091,7 +3091,8 @@ static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n-\treturn handle_content_merge(opt, path, was_dirty(opt, path),\n+\tstruct merge_file_info mfi;\n+\treturn handle_content_merge(&mfi, opt, path, was_dirty(opt, path),\n \t\t\t\t    o, a, b, ci);\n }\n \n@@ -3256,8 +3257,10 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t    a, b);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n+\t\t\tstruct merge_file_info mfi;\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(opt, path, is_dirty,\n+\t\t\tclean_merge = handle_content_merge(&mfi, opt, path,\n+\t\t\t\t\t\t\t   is_dirty,\n \t\t\t\t\t\t\t   o, a, b, NULL);\n \t\t}\n \t} else if (!o_valid && !a_valid && !b_valid) {\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373213","messageId":"20190405150026.5260-13-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 12/15] t6043: fix copied test description to match its purpose","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:23Z","receivedAt":"2019-04-05T15:01:12Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-rename-directories.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh\nindex 62c564707b..fe205be607 100755\n--- a/t/t6043-merge-rename-directories.sh\n+++ b/t/t6043-merge-rename-directories.sh\n@@ -3910,7 +3910,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '\n #         To which, I can do no more than shrug my shoulders and say that\n #         even simple rules give weird results when given weird inputs.\n \n-test_expect_success '12b-setup: Moving one directory hierarchy into another' '\n+test_expect_success '12b-setup: Moving two directory hierarchies into each other' '\n \ttest_create_repo 12b &&\n \t(\n \t\tcd 12b &&\n@@ -3940,7 +3940,7 @@ test_expect_success '12b-setup: Moving one directory hierarchy into another' '\n \t)\n '\n \n-test_expect_success '12b-check: Moving one directory hierarchy into another' '\n+test_expect_success '12b-check: Moving two directory hierarchies into each other' '\n \t(\n \t\tcd 12b &&\n \n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373214","messageId":"20190405150026.5260-14-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 13/15] merge-recursive: track information associated with directory renames","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:24Z","receivedAt":"2019-04-05T15:01:12Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Directory rename detection previously silently applied.  In order to\nallow printing information about paths that changed or printing a\nconflict notification (and only doing so near other potential conflict\nmessages associated with the paths), save this information inside the\nrename struct for later use.  A subsequent patch will make use of the\nadditional information.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1d2c9e1772..938a526b20 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -207,8 +207,16 @@ struct stage_data {\n };\n \n struct rename {\n+\tunsigned processed:1;\n \tstruct diff_filepair *pair;\n \tconst char *branch; /* branch that the rename occurred on */\n+\t/*\n+\t * If directory rename detection affected this rename, what was its\n+\t * original type ('A' or 'R') and it's original destination before\n+\t * the directory rename (otherwise, '\\0' and NULL for these two vars).\n+\t */\n+\tchar dir_rename_original_type;\n+\tchar *dir_rename_original_dest;\n \t/*\n \t * Purpose of src_entry and dst_entry:\n \t *\n@@ -230,8 +238,6 @@ struct rename {\n \t */\n \tstruct stage_data *src_entry;\n \tstruct stage_data *dst_entry;\n-\tunsigned add_turned_into_rename:1;\n-\tunsigned processed:1;\n };\n \n struct rename_conflict_info {\n@@ -2484,16 +2490,18 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t\t       &re->dst_entry->stages[stage].oid,\n \t\t       &re->dst_entry->stages[stage].mode);\n \n-\t/* Update pair status */\n-\tif (pair->status == 'A') {\n-\t\t/*\n-\t\t * Recording rename information for this add makes it look\n-\t\t * like a rename/delete conflict.  Make sure we can\n-\t\t * correctly handle this as an add that was moved to a new\n-\t\t * directory instead of reporting a rename/delete conflict.\n-\t\t */\n-\t\tre->add_turned_into_rename = 1;\n-\t}\n+\t/*\n+\t * Record the original change status (or 'type' of change).  If it\n+\t * was originally an add ('A'), this lets us differentiate later\n+\t * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they\n+\t * otherwise look the same).  If it was originally a rename ('R'),\n+\t * this lets us remember and report accurately about the transitive\n+\t * renaming that occurred via the directory rename detection.  Also,\n+\t * record the original destination name.\n+\t */\n+\tre->dir_rename_original_type = pair->status;\n+\tre->dir_rename_original_dest = pair->two->path;\n+\n \t/*\n \t * We don't actually look at pair->status again, but it seems\n \t * pedagogically correct to adjust it.\n@@ -2556,9 +2564,10 @@ static struct string_list *get_renames(struct merge_options *opt,\n \n \t\tre = xmalloc(sizeof(*re));\n \t\tre->processed = 0;\n-\t\tre->add_turned_into_rename = 0;\n \t\tre->pair = pair;\n \t\tre->branch = branch;\n+\t\tre->dir_rename_original_type = '\\0';\n+\t\tre->dir_rename_original_dest = NULL;\n \t\titem = string_list_lookup(entries, re->pair->one->path);\n \t\tif (!item)\n \t\t\tre->src_entry = insert_stage_data(re->pair->one->path,\n@@ -2726,7 +2735,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\ttry_merge = 0;\n \n \t\t\tif (oid_eq(&src_other.oid, &null_oid) &&\n-\t\t\t    ren1->add_turned_into_rename) {\n+\t\t\t    ren1->dir_rename_original_type == 'A') {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n \t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373215","messageId":"20190405150026.5260-3-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 02/15] merge-recursive: rename merge_options argument from 'o' to 'opt'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:13Z","receivedAt":"2019-04-05T15:01:13Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The name 'o' was used for the merge_options struct pointer taken by many\nfunctions, but in a few places it was named 'opt'.  Several functions\nthat didn't need merge_options instead used 'o' for a diff_filespec\nargument or local.  Some functions needed both an inconsistently either\nrenamed the merge_options to 'opt' or the diff_filespec to 'one'.  I\nwant to remove the weird split in the codebase between using a\ndiff_filespec and a pair of (oid,mode) values in favor of using a\ndiff_filespec everywhere, but that dramatically increases the number of\ncases where we want to use 'o' as a diff_filespec.  Rename the\nmerge_options argument to 'opt' to make room.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 984 +++++++++++++++++++++++-----------------------\n 1 file changed, 492 insertions(+), 492 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fcf37be2a7..09b76d596e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -115,32 +115,32 @@ static void collision_init(struct hashmap *map)\n \thashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);\n }\n \n-static void flush_output(struct merge_options *o)\n+static void flush_output(struct merge_options *opt)\n {\n-\tif (o->buffer_output < 2 && o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n+\tif (opt->buffer_output < 2 && opt->obuf.len) {\n+\t\tfputs(opt->obuf.buf, stdout);\n+\t\tstrbuf_reset(&opt->obuf);\n \t}\n }\n \n-static int err(struct merge_options *o, const char *err, ...)\n+static int err(struct merge_options *opt, const char *err, ...)\n {\n \tva_list params;\n \n-\tif (o->buffer_output < 2)\n-\t\tflush_output(o);\n+\tif (opt->buffer_output < 2)\n+\t\tflush_output(opt);\n \telse {\n-\t\tstrbuf_complete(&o->obuf, '\\n');\n-\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t\tstrbuf_complete(&opt->obuf, '\\n');\n+\t\tstrbuf_addstr(&opt->obuf, \"error: \");\n \t}\n \tva_start(params, err);\n-\tstrbuf_vaddf(&o->obuf, err, params);\n+\tstrbuf_vaddf(&opt->obuf, err, params);\n \tva_end(params);\n-\tif (o->buffer_output > 1)\n-\t\tstrbuf_addch(&o->obuf, '\\n');\n+\tif (opt->buffer_output > 1)\n+\t\tstrbuf_addch(&opt->obuf, '\\n');\n \telse {\n-\t\terror(\"%s\", o->obuf.buf);\n-\t\tstrbuf_reset(&o->obuf);\n+\t\terror(\"%s\", opt->obuf.buf);\n+\t\tstrbuf_reset(&opt->obuf);\n \t}\n \n \treturn -1;\n@@ -228,7 +228,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t\t\t\t\t      const char *branch2,\n \t\t\t\t\t      struct stage_data *dst_entry1,\n \t\t\t\t\t      struct stage_data *dst_entry2,\n-\t\t\t\t\t      struct merge_options *o,\n+\t\t\t\t\t      struct merge_options *opt,\n \t\t\t\t\t      struct stage_data *src_entry1,\n \t\t\t\t\t      struct stage_data *src_entry2)\n {\n@@ -239,15 +239,15 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t * When we have two renames involved, it's easiest to get the\n \t * correct things into stage 2 and 3, and to make sure that the\n \t * content merge puts HEAD before the other branch if we just\n-\t * ensure that branch1 == o->branch1.  So, simply flip arguments\n+\t * ensure that branch1 == opt->branch1.  So, simply flip arguments\n \t * around if we don't have that.\n \t */\n-\tif (dst_entry2 && branch1 != o->branch1) {\n+\tif (dst_entry2 && branch1 != opt->branch1) {\n \t\tsetup_rename_conflict_info(rename_type,\n \t\t\t\t\t   pair2,      pair1,\n \t\t\t\t\t   branch2,    branch1,\n \t\t\t\t\t   dst_entry2, dst_entry1,\n-\t\t\t\t\t   o,\n+\t\t\t\t\t   opt,\n \t\t\t\t\t   src_entry2, src_entry1);\n \t\treturn;\n \t}\n@@ -276,7 +276,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t */\n \tif (rename_type == RENAME_ADD ||\n \t    rename_type == RENAME_TWO_FILES_TO_ONE) {\n-\t\tostage1 = o->branch1 == branch1 ? 3 : 2;\n+\t\tostage1 = opt->branch1 == branch1 ? 3 : 2;\n \n \t\tci->ren1_other.path = pair1->one->path;\n \t\toidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);\n@@ -292,67 +292,67 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \t}\n }\n \n-static int show(struct merge_options *o, int v)\n+static int show(struct merge_options *opt, int v)\n {\n-\treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n+\treturn (!opt->call_depth && opt->verbosity >= v) || opt->verbosity >= 5;\n }\n \n __attribute__((format (printf, 3, 4)))\n-static void output(struct merge_options *o, int v, const char *fmt, ...)\n+static void output(struct merge_options *opt, int v, const char *fmt, ...)\n {\n \tva_list ap;\n \n-\tif (!show(o, v))\n+\tif (!show(opt, v))\n \t\treturn;\n \n-\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n+\tstrbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);\n \n \tva_start(ap, fmt);\n-\tstrbuf_vaddf(&o->obuf, fmt, ap);\n+\tstrbuf_vaddf(&opt->obuf, fmt, ap);\n \tva_end(ap);\n \n-\tstrbuf_addch(&o->obuf, '\\n');\n-\tif (!o->buffer_output)\n-\t\tflush_output(o);\n+\tstrbuf_addch(&opt->obuf, '\\n');\n+\tif (!opt->buffer_output)\n+\t\tflush_output(opt);\n }\n \n-static void output_commit_title(struct merge_options *o, struct commit *commit)\n+static void output_commit_title(struct merge_options *opt, struct commit *commit)\n {\n \tstruct merge_remote_desc *desc;\n \n-\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n+\tstrbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);\n \tdesc = merge_remote_util(commit);\n \tif (desc)\n-\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\", desc->name);\n+\t\tstrbuf_addf(&opt->obuf, \"virtual %s\\n\", desc->name);\n \telse {\n-\t\tstrbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,\n+\t\tstrbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,\n \t\t\t\t\t DEFAULT_ABBREV);\n-\t\tstrbuf_addch(&o->obuf, ' ');\n+\t\tstrbuf_addch(&opt->obuf, ' ');\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tstrbuf_addstr(&o->obuf, _(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addstr(&opt->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&opt->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n-\tflush_output(o);\n+\tflush_output(opt);\n }\n \n-static int add_cacheinfo(struct merge_options *o,\n+static int add_cacheinfo(struct merge_options *opt,\n \t\t\t unsigned int mode, const struct object_id *oid,\n \t\t\t const char *path, int stage, int refresh, int options)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \tstruct cache_entry *ce;\n \tint ret;\n \n \tce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);\n \tif (!ce)\n-\t\treturn err(o, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n+\t\treturn err(opt, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n \n \tret = add_index_entry(istate, ce, options);\n \tif (refresh) {\n@@ -361,7 +361,7 @@ static int add_cacheinfo(struct merge_options *o,\n \t\tnce = refresh_cache_entry(istate, ce,\n \t\t\t\t\t  CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);\n \t\tif (!nce)\n-\t\t\treturn err(o, _(\"add_cacheinfo failed to refresh for path '%s'; merge aborting.\"), path);\n+\t\t\treturn err(opt, _(\"add_cacheinfo failed to refresh for path '%s'; merge aborting.\"), path);\n \t\tif (nce != ce)\n \t\t\tret = add_index_entry(istate, nce, options);\n \t}\n@@ -374,7 +374,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)\n \tinit_tree_desc(desc, tree->buffer, tree->size);\n }\n \n-static int unpack_trees_start(struct merge_options *o,\n+static int unpack_trees_start(struct merge_options *opt,\n \t\t\t      struct tree *common,\n \t\t\t      struct tree *head,\n \t\t\t      struct tree *merge)\n@@ -383,49 +383,49 @@ static int unpack_trees_start(struct merge_options *o,\n \tstruct tree_desc t[3];\n \tstruct index_state tmp_index = { NULL };\n \n-\tmemset(&o->unpack_opts, 0, sizeof(o->unpack_opts));\n-\tif (o->call_depth)\n-\t\to->unpack_opts.index_only = 1;\n+\tmemset(&opt->unpack_opts, 0, sizeof(opt->unpack_opts));\n+\tif (opt->call_depth)\n+\t\topt->unpack_opts.index_only = 1;\n \telse\n-\t\to->unpack_opts.update = 1;\n-\to->unpack_opts.merge = 1;\n-\to->unpack_opts.head_idx = 2;\n-\to->unpack_opts.fn = threeway_merge;\n-\to->unpack_opts.src_index = o->repo->index;\n-\to->unpack_opts.dst_index = &tmp_index;\n-\to->unpack_opts.aggressive = !merge_detect_rename(o);\n-\tsetup_unpack_trees_porcelain(&o->unpack_opts, \"merge\");\n+\t\topt->unpack_opts.update = 1;\n+\topt->unpack_opts.merge = 1;\n+\topt->unpack_opts.head_idx = 2;\n+\topt->unpack_opts.fn = threeway_merge;\n+\topt->unpack_opts.src_index = opt->repo->index;\n+\topt->unpack_opts.dst_index = &tmp_index;\n+\topt->unpack_opts.aggressive = !merge_detect_rename(opt);\n+\tsetup_unpack_trees_porcelain(&opt->unpack_opts, \"merge\");\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n \tinit_tree_desc_from_tree(t+2, merge);\n \n-\trc = unpack_trees(3, t, &o->unpack_opts);\n-\tcache_tree_free(&o->repo->index->cache_tree);\n+\trc = unpack_trees(3, t, &opt->unpack_opts);\n+\tcache_tree_free(&opt->repo->index->cache_tree);\n \n \t/*\n-\t * Update o->repo->index to match the new results, AFTER saving a copy\n-\t * in o->orig_index.  Update src_index to point to the saved copy.\n+\t * Update opt->repo->index to match the new results, AFTER saving a copy\n+\t * in opt->orig_index.  Update src_index to point to the saved copy.\n \t * (verify_uptodate() checks src_index, and the original index is\n \t * the one that had the necessary modification timestamps.)\n \t */\n-\to->orig_index = *o->repo->index;\n-\t*o->repo->index = tmp_index;\n-\to->unpack_opts.src_index = &o->orig_index;\n+\topt->orig_index = *opt->repo->index;\n+\t*opt->repo->index = tmp_index;\n+\topt->unpack_opts.src_index = &opt->orig_index;\n \n \treturn rc;\n }\n \n-static void unpack_trees_finish(struct merge_options *o)\n+static void unpack_trees_finish(struct merge_options *opt)\n {\n-\tdiscard_index(&o->orig_index);\n-\tclear_unpack_trees_porcelain(&o->unpack_opts);\n+\tdiscard_index(&opt->orig_index);\n+\tclear_unpack_trees_porcelain(&opt->unpack_opts);\n }\n \n-struct tree *write_tree_from_memory(struct merge_options *o)\n+struct tree *write_tree_from_memory(struct merge_options *opt)\n {\n \tstruct tree *result = NULL;\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \n \tif (unmerged_index(istate)) {\n \t\tint i;\n@@ -444,11 +444,11 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(istate->cache_tree) &&\n \t    cache_tree_update(istate, 0) < 0) {\n-\t\terr(o, _(\"error building trees\"));\n+\t\terr(opt, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n-\tresult = lookup_tree(o->repo, &istate->cache_tree->oid);\n+\tresult = lookup_tree(opt->repo, &istate->cache_tree->oid);\n \n \treturn result;\n }\n@@ -459,24 +459,24 @@ static int save_files_dirs(const struct object_id *oid,\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n-\tstruct merge_options *o = context;\n+\tstruct merge_options *opt = context;\n \n \tstrbuf_addstr(base, path);\n \n \tFLEX_ALLOC_MEM(entry, path, base->buf, base->len);\n \thashmap_entry_init(entry, path_hash(entry->path));\n-\thashmap_add(&o->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, entry);\n \n \tstrbuf_setlen(base, baselen);\n \treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n }\n \n-static void get_files_dirs(struct merge_options *o, struct tree *tree)\n+static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n \tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n-\t\t\t    &match_all, save_files_dirs, o);\n+\t\t\t    &match_all, save_files_dirs, opt);\n }\n \n static int get_tree_entry_if_blob(const struct object_id *tree,\n@@ -573,7 +573,7 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \treturn onelen - twolen;\n }\n \n-static void record_df_conflict_files(struct merge_options *o,\n+static void record_df_conflict_files(struct merge_options *opt,\n \t\t\t\t     struct string_list *entries)\n {\n \t/* If there is a D/F conflict and the file for such a conflict\n@@ -598,7 +598,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \t * If we're merging merge-bases, we don't want to bother with\n \t * any working directory changes.\n \t */\n-\tif (o->call_depth)\n+\tif (opt->call_depth)\n \t\treturn;\n \n \t/* Ensure D/F conflicts are adjacent in the entries list. */\n@@ -610,7 +610,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \tdf_sorted_entries.cmp = string_list_df_name_compare;\n \tstring_list_sort(&df_sorted_entries);\n \n-\tstring_list_clear(&o->df_conflict_file_set, 1);\n+\tstring_list_clear(&opt->df_conflict_file_set, 1);\n \tfor (i = 0; i < df_sorted_entries.nr; i++) {\n \t\tconst char *path = df_sorted_entries.items[i].string;\n \t\tint len = strlen(path);\n@@ -626,7 +626,7 @@ static void record_df_conflict_files(struct merge_options *o,\n \t\t    len > last_len &&\n \t\t    memcmp(path, last_file, last_len) == 0 &&\n \t\t    path[last_len] == '/') {\n-\t\t\tstring_list_insert(&o->df_conflict_file_set, last_file);\n+\t\t\tstring_list_insert(&opt->df_conflict_file_set, last_file);\n \t\t}\n \n \t\t/*\n@@ -717,20 +717,20 @@ static void update_entry(struct stage_data *entry,\n \toidcpy(&entry->stages[3].oid, &b->oid);\n }\n \n-static int remove_file(struct merge_options *o, int clean,\n+static int remove_file(struct merge_options *opt, int clean,\n \t\t       const char *path, int no_wd)\n {\n-\tint update_cache = o->call_depth || clean;\n-\tint update_working_directory = !o->call_depth && !no_wd;\n+\tint update_cache = opt->call_depth || clean;\n+\tint update_working_directory = !opt->call_depth && !no_wd;\n \n \tif (update_cache) {\n-\t\tif (remove_file_from_index(o->repo->index, path))\n+\t\tif (remove_file_from_index(opt->repo->index, path))\n \t\t\treturn -1;\n \t}\n \tif (update_working_directory) {\n \t\tif (ignore_case) {\n \t\t\tstruct cache_entry *ce;\n-\t\t\tce = index_file_exists(o->repo->index, path, strlen(path),\n+\t\t\tce = index_file_exists(opt->repo->index, path, strlen(path),\n \t\t\t\t\t       ignore_case);\n \t\t\tif (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))\n \t\t\t\treturn 0;\n@@ -751,7 +751,7 @@ static void add_flattened_path(struct strbuf *out, const char *s)\n \t\t\tout->buf[i] = '_';\n }\n \n-static char *unique_path(struct merge_options *o, const char *path, const char *branch)\n+static char *unique_path(struct merge_options *opt, const char *path, const char *branch)\n {\n \tstruct path_hashmap_entry *entry;\n \tstruct strbuf newpath = STRBUF_INIT;\n@@ -762,16 +762,16 @@ static char *unique_path(struct merge_options *o, const char *path, const char *\n \tadd_flattened_path(&newpath, branch);\n \n \tbase_len = newpath.len;\n-\twhile (hashmap_get_from_hash(&o->current_file_dir_set,\n+\twhile (hashmap_get_from_hash(&opt->current_file_dir_set,\n \t\t\t\t     path_hash(newpath.buf), newpath.buf) ||\n-\t       (!o->call_depth && file_exists(newpath.buf))) {\n+\t       (!opt->call_depth && file_exists(newpath.buf))) {\n \t\tstrbuf_setlen(&newpath, base_len);\n \t\tstrbuf_addf(&newpath, \"_%d\", suffix++);\n \t}\n \n \tFLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);\n \thashmap_entry_init(entry, path_hash(entry->path));\n-\thashmap_add(&o->current_file_dir_set, entry);\n+\thashmap_add(&opt->current_file_dir_set, entry);\n \treturn strbuf_detach(&newpath, NULL);\n }\n \n@@ -810,10 +810,10 @@ static int dir_in_way(struct index_state *istate, const char *path,\n  * Returns whether path was tracked in the index before the merge started,\n  * and its oid and mode match the specified values\n  */\n-static int was_tracked_and_matches(struct merge_options *o, const char *path,\n+static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n \t\t\t\t   const struct object_id *oid, unsigned mode)\n {\n-\tint pos = index_name_pos(&o->orig_index, path, strlen(path));\n+\tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \tstruct cache_entry *ce;\n \n \tif (0 > pos)\n@@ -821,16 +821,16 @@ static int was_tracked_and_matches(struct merge_options *o, const char *path,\n \t\treturn 0;\n \n \t/* See if the file we were tracking before matches */\n-\tce = o->orig_index.cache[pos];\n+\tce = opt->orig_index.cache[pos];\n \treturn (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);\n }\n \n /*\n  * Returns whether path was tracked in the index before the merge started\n  */\n-static int was_tracked(struct merge_options *o, const char *path)\n+static int was_tracked(struct merge_options *opt, const char *path)\n {\n-\tint pos = index_name_pos(&o->orig_index, path, strlen(path));\n+\tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \n \tif (0 <= pos)\n \t\t/* we were tracking this path before the merge */\n@@ -839,13 +839,13 @@ static int was_tracked(struct merge_options *o, const char *path)\n \treturn 0;\n }\n \n-static int would_lose_untracked(struct merge_options *o, const char *path)\n+static int would_lose_untracked(struct merge_options *opt, const char *path)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \n \t/*\n \t * This may look like it can be simplified to:\n-\t *   return !was_tracked(o, path) && file_exists(path)\n+\t *   return !was_tracked(opt, path) && file_exists(path)\n \t * but it can't.  This function needs to know whether path was in\n \t * the working tree due to EITHER having been tracked in the index\n \t * before the merge OR having been put into the working copy and\n@@ -882,38 +882,38 @@ static int would_lose_untracked(struct merge_options *o, const char *path)\n \treturn file_exists(path);\n }\n \n-static int was_dirty(struct merge_options *o, const char *path)\n+static int was_dirty(struct merge_options *opt, const char *path)\n {\n \tstruct cache_entry *ce;\n \tint dirty = 1;\n \n-\tif (o->call_depth || !was_tracked(o, path))\n+\tif (opt->call_depth || !was_tracked(opt, path))\n \t\treturn !dirty;\n \n-\tce = index_file_exists(o->unpack_opts.src_index,\n+\tce = index_file_exists(opt->unpack_opts.src_index,\n \t\t\t       path, strlen(path), ignore_case);\n-\tdirty = verify_uptodate(ce, &o->unpack_opts) != 0;\n+\tdirty = verify_uptodate(ce, &opt->unpack_opts) != 0;\n \treturn dirty;\n }\n \n-static int make_room_for_path(struct merge_options *o, const char *path)\n+static int make_room_for_path(struct merge_options *opt, const char *path)\n {\n \tint status, i;\n \tconst char *msg = _(\"failed to create path '%s'%s\");\n \n \t/* Unlink any D/F conflict files that are in the way */\n-\tfor (i = 0; i < o->df_conflict_file_set.nr; i++) {\n-\t\tconst char *df_path = o->df_conflict_file_set.items[i].string;\n+\tfor (i = 0; i < opt->df_conflict_file_set.nr; i++) {\n+\t\tconst char *df_path = opt->df_conflict_file_set.items[i].string;\n \t\tsize_t pathlen = strlen(path);\n \t\tsize_t df_pathlen = strlen(df_path);\n \t\tif (df_pathlen < pathlen &&\n \t\t    path[df_pathlen] == '/' &&\n \t\t    strncmp(path, df_path, df_pathlen) == 0) {\n-\t\t\toutput(o, 3,\n+\t\t\toutput(opt, 3,\n \t\t\t       _(\"Removing %s to make room for subdirectory\\n\"),\n \t\t\t       df_path);\n \t\t\tunlink(df_path);\n-\t\t\tunsorted_string_list_delete_item(&o->df_conflict_file_set,\n+\t\t\tunsorted_string_list_delete_item(&opt->df_conflict_file_set,\n \t\t\t\t\t\t\t i, 0);\n \t\t\tbreak;\n \t\t}\n@@ -924,16 +924,16 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn err(o, msg, path, \"\");\n+\t\t\treturn err(opt, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(opt, msg, path, \"\");\n \t}\n \n \t/*\n \t * Do not unlink a file in the work tree if we are not\n \t * tracking it.\n \t */\n-\tif (would_lose_untracked(o, path))\n-\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n+\tif (would_lose_untracked(opt, path))\n+\t\treturn err(opt, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t   path);\n \n \t/* Successful unlink is good.. */\n@@ -943,10 +943,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(opt, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static int update_file_flags(struct merge_options *o,\n+static int update_file_flags(struct merge_options *opt,\n \t\t\t     const struct object_id *oid,\n \t\t\t     unsigned mode,\n \t\t\t     const char *path,\n@@ -955,7 +955,7 @@ static int update_file_flags(struct merge_options *o,\n {\n \tint ret = 0;\n \n-\tif (o->call_depth)\n+\tif (opt->call_depth)\n \t\tupdate_wd = 0;\n \n \tif (update_wd) {\n@@ -975,21 +975,21 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_object_file(oid, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n-\t\t\tif (convert_to_working_tree(o->repo->index, path, buf, size, &strbuf)) {\n+\t\t\tif (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {\n \t\t\t\tfree(buf);\n \t\t\t\tsize = strbuf.len;\n \t\t\t\tbuf = strbuf_detach(&strbuf, NULL);\n \t\t\t}\n \t\t}\n \n-\t\tif (make_room_for_path(o, path) < 0) {\n+\t\tif (make_room_for_path(opt, path) < 0) {\n \t\t\tupdate_wd = 0;\n \t\t\tgoto free_buf;\n \t\t}\n@@ -1001,7 +1001,7 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\tret = err(opt, _(\"failed to open '%s': %s\"),\n \t\t\t\t\t  path, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n@@ -1012,11 +1012,11 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\tret = err(opt, _(\"failed to symlink '%s': %s\"),\n \t\t\t\t\t  path, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = err(o,\n+\t\t\tret = err(opt,\n \t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t\t  mode, oid_to_hex(oid), path);\n \tfree_buf:\n@@ -1024,19 +1024,19 @@ static int update_file_flags(struct merge_options *o,\n \t}\n update_index:\n \tif (!ret && update_cache)\n-\t\tif (add_cacheinfo(o, mode, oid, path, 0, update_wd,\n+\t\tif (add_cacheinfo(opt, mode, oid, path, 0, update_wd,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD))\n \t\t\treturn -1;\n \treturn ret;\n }\n \n-static int update_file(struct merge_options *o,\n+static int update_file(struct merge_options *opt,\n \t\t       int clean,\n \t\t       const struct object_id *oid,\n \t\t       unsigned mode,\n \t\t       const char *path)\n {\n-\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(opt, oid, mode, path, opt->call_depth || clean, !opt->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1048,7 +1048,7 @@ struct merge_file_info {\n \t\t merge:1;\n };\n \n-static int merge_3way(struct merge_options *o,\n+static int merge_3way(struct merge_options *opt,\n \t\t      mmbuffer_t *result_buf,\n \t\t      const struct diff_filespec *one,\n \t\t      const struct diff_filespec *a,\n@@ -1062,15 +1062,15 @@ static int merge_3way(struct merge_options *o,\n \tchar *base_name, *name1, *name2;\n \tint merge_status;\n \n-\tll_opts.renormalize = o->renormalize;\n+\tll_opts.renormalize = opt->renormalize;\n \tll_opts.extra_marker_size = extra_marker_size;\n-\tll_opts.xdl_opts = o->xdl_opts;\n+\tll_opts.xdl_opts = opt->xdl_opts;\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\tll_opts.virtual_ancestor = 1;\n \t\tll_opts.variant = 0;\n \t} else {\n-\t\tswitch (o->recursive_variant) {\n+\t\tswitch (opt->recursive_variant) {\n \t\tcase MERGE_RECURSIVE_OURS:\n \t\t\tll_opts.variant = XDL_MERGE_FAVOR_OURS;\n \t\t\tbreak;\n@@ -1084,14 +1084,14 @@ static int merge_3way(struct merge_options *o,\n \t}\n \n \tif (strcmp(a->path, b->path) ||\n-\t    (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n-\t\tbase_name = o->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s:%s\", o->ancestor, one->path);\n+\t    (opt->ancestor != NULL && strcmp(a->path, one->path) != 0)) {\n+\t\tbase_name = opt->ancestor == NULL ? NULL :\n+\t\t\tmkpathdup(\"%s:%s\", opt->ancestor, one->path);\n \t\tname1 = mkpathdup(\"%s:%s\", branch1, a->path);\n \t\tname2 = mkpathdup(\"%s:%s\", branch2, b->path);\n \t} else {\n-\t\tbase_name = o->ancestor == NULL ? NULL :\n-\t\t\tmkpathdup(\"%s\", o->ancestor);\n+\t\tbase_name = opt->ancestor == NULL ? NULL :\n+\t\t\tmkpathdup(\"%s\", opt->ancestor);\n \t\tname1 = mkpathdup(\"%s\", branch1);\n \t\tname2 = mkpathdup(\"%s\", branch2);\n \t}\n@@ -1102,7 +1102,7 @@ static int merge_3way(struct merge_options *o,\n \n \tmerge_status = ll_merge(result_buf, a->path, &orig, base_name,\n \t\t\t\t&src1, name1, &src2, name2,\n-\t\t\t\to->repo->index, &ll_opts);\n+\t\t\t\topt->repo->index, &ll_opts);\n \n \tfree(base_name);\n \tfree(name1);\n@@ -1184,7 +1184,7 @@ static void print_commit(struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n-static int merge_submodule(struct merge_options *o,\n+static int merge_submodule(struct merge_options *opt,\n \t\t\t   struct object_id *result, const char *path,\n \t\t\t   const struct object_id *base, const struct object_id *a,\n \t\t\t   const struct object_id *b)\n@@ -1194,7 +1194,7 @@ static int merge_submodule(struct merge_options *o,\n \tstruct object_array merges;\n \n \tint i;\n-\tint search = !o->call_depth;\n+\tint search = !opt->call_depth;\n \n \t/* store a in result in case we fail */\n \toidcpy(result, a);\n@@ -1208,32 +1208,32 @@ static int merge_submodule(struct merge_options *o,\n \t\treturn 0;\n \n \tif (add_submodule_odb(path)) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (not checked out)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (not checked out)\"), path);\n \t\treturn 0;\n \t}\n \n-\tif (!(commit_base = lookup_commit_reference(o->repo, base)) ||\n-\t    !(commit_a = lookup_commit_reference(o->repo, a)) ||\n-\t    !(commit_b = lookup_commit_reference(o->repo, b))) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (commits not present)\"), path);\n+\tif (!(commit_base = lookup_commit_reference(opt->repo, base)) ||\n+\t    !(commit_a = lookup_commit_reference(opt->repo, a)) ||\n+\t    !(commit_b = lookup_commit_reference(opt->repo, b))) {\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (commits not present)\"), path);\n \t\treturn 0;\n \t}\n \n \t/* check whether both changes are forward */\n \tif (!in_merge_bases(commit_base, commit_a) ||\n \t    !in_merge_bases(commit_base, commit_b)) {\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (commits don't follow merge-base)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (commits don't follow merge-base)\"), path);\n \t\treturn 0;\n \t}\n \n \t/* Case #1: a is contained in b or vice versa */\n \tif (in_merge_bases(commit_a, commit_b)) {\n \t\toidcpy(result, b);\n-\t\tif (show(o, 3)) {\n-\t\t\toutput(o, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n-\t\t\toutput_commit_title(o, commit_b);\n-\t\t} else if (show(o, 2))\n-\t\t\toutput(o, 2, _(\"Fast-forwarding submodule %s\"), path);\n+\t\tif (show(opt, 3)) {\n+\t\t\toutput(opt, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n+\t\t\toutput_commit_title(opt, commit_b);\n+\t\t} else if (show(opt, 2))\n+\t\t\toutput(opt, 2, _(\"Fast-forwarding submodule %s\"), path);\n \t\telse\n \t\t\t; /* no output */\n \n@@ -1241,11 +1241,11 @@ static int merge_submodule(struct merge_options *o,\n \t}\n \tif (in_merge_bases(commit_b, commit_a)) {\n \t\toidcpy(result, a);\n-\t\tif (show(o, 3)) {\n-\t\t\toutput(o, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n-\t\t\toutput_commit_title(o, commit_a);\n-\t\t} else if (show(o, 2))\n-\t\t\toutput(o, 2, _(\"Fast-forwarding submodule %s\"), path);\n+\t\tif (show(opt, 3)) {\n+\t\t\toutput(opt, 3, _(\"Fast-forwarding submodule %s to the following commit:\"), path);\n+\t\t\toutput_commit_title(opt, commit_a);\n+\t\t} else if (show(opt, 2))\n+\t\t\toutput(opt, 2, _(\"Fast-forwarding submodule %s\"), path);\n \t\telse\n \t\t\t; /* no output */\n \n@@ -1264,18 +1264,18 @@ static int merge_submodule(struct merge_options *o,\n \t\treturn 0;\n \n \t/* find commit which merges them */\n-\tparent_count = find_first_merges(o->repo, &merges, path,\n+\tparent_count = find_first_merges(opt->repo, &merges, path,\n \t\t\t\t\t commit_a, commit_b);\n \tswitch (parent_count) {\n \tcase 0:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (merge following commits not found)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (merge following commits not found)\"), path);\n \t\tbreak;\n \n \tcase 1:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (not fast-forward)\"), path);\n-\t\toutput(o, 2, _(\"Found a possible merge resolution for the submodule:\\n\"));\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (not fast-forward)\"), path);\n+\t\toutput(opt, 2, _(\"Found a possible merge resolution for the submodule:\\n\"));\n \t\tprint_commit((struct commit *) merges.objects[0].item);\n-\t\toutput(o, 2, _(\n+\t\toutput(opt, 2, _(\n \t\t       \"If this is correct simply add it to the index \"\n \t\t       \"for example\\n\"\n \t\t       \"by using:\\n\\n\"\n@@ -1285,7 +1285,7 @@ static int merge_submodule(struct merge_options *o,\n \t\tbreak;\n \n \tdefault:\n-\t\toutput(o, 1, _(\"Failed to merge submodule %s (multiple merges found)\"), path);\n+\t\toutput(opt, 1, _(\"Failed to merge submodule %s (multiple merges found)\"), path);\n \t\tfor (i = 0; i < merges.nr; i++)\n \t\t\tprint_commit((struct commit *) merges.objects[i].item);\n \t}\n@@ -1294,7 +1294,7 @@ static int merge_submodule(struct merge_options *o,\n \treturn 0;\n }\n \n-static int merge_mode_and_contents(struct merge_options *o,\n+static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t   const struct diff_filespec *b,\n@@ -1304,13 +1304,13 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\t\t   const int extra_marker_size,\n \t\t\t\t   struct merge_file_info *result)\n {\n-\tif (o->branch1 != branch1) {\n+\tif (opt->branch1 != branch1) {\n \t\t/*\n \t\t * It's weird getting a reverse merge with HEAD on the bottom\n \t\t * side of the conflict markers and the other branch on the\n \t\t * top.  Fix that.\n \t\t */\n-\t\treturn merge_mode_and_contents(o, one, b, a,\n+\t\treturn merge_mode_and_contents(opt, one, b, a,\n \t\t\t\t\t       filename,\n \t\t\t\t\t       branch2, branch1,\n \t\t\t\t\t       extra_marker_size, result);\n@@ -1353,17 +1353,17 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n \n-\t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n+\t\t\tmerge_status = merge_3way(opt, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2,\n \t\t\t\t\t\t  extra_marker_size);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(opt, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret &&\n \t\t\t    write_object_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t      blob_type, &result->oid))\n-\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n+\t\t\t\tret = err(opt, _(\"Unable to add %s to database\"),\n \t\t\t\t\t  a->path);\n \n \t\t\tfree(result_buf.ptr);\n@@ -1371,13 +1371,13 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult->clean = merge_submodule(o, &result->oid,\n+\t\t\tresult->clean = merge_submodule(opt, &result->oid,\n \t\t\t\t\t\t\tone->path,\n \t\t\t\t\t\t\t&one->oid,\n \t\t\t\t\t\t\t&a->oid,\n \t\t\t\t\t\t\t&b->oid);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\tswitch (o->recursive_variant) {\n+\t\t\tswitch (opt->recursive_variant) {\n \t\t\tcase MERGE_RECURSIVE_NORMAL:\n \t\t\t\toidcpy(&result->oid, &a->oid);\n \t\t\t\tif (!oid_eq(&a->oid, &b->oid))\n@@ -1395,12 +1395,12 @@ static int merge_mode_and_contents(struct merge_options *o,\n \t}\n \n \tif (result->merge)\n-\t\toutput(o, 2, _(\"Auto-merging %s\"), filename);\n+\t\toutput(opt, 2, _(\"Auto-merging %s\"), filename);\n \n \treturn 0;\n }\n \n-static int handle_rename_via_dir(struct merge_options *o,\n+static int handle_rename_via_dir(struct merge_options *opt,\n \t\t\t\t struct diff_filepair *pair,\n \t\t\t\t const char *rename_branch)\n {\n@@ -1412,10 +1412,10 @@ static int handle_rename_via_dir(struct merge_options *o,\n \t */\n \tconst struct diff_filespec *dest = pair->two;\n \n-\tif (!o->call_depth && would_lose_untracked(o, dest->path)) {\n-\t\tchar *alt_path = unique_path(o, dest->path, rename_branch);\n+\tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n+\t\tchar *alt_path = unique_path(opt, dest->path, rename_branch);\n \n-\t\toutput(o, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n+\t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n \t\t\t       \"writing to %s instead.\"),\n \t\t       dest->path, alt_path);\n \t\t/*\n@@ -1423,21 +1423,21 @@ static int handle_rename_via_dir(struct merge_options *o,\n \t\t * index.  Instead, write to dest->path for the index but\n \t\t * only at the higher appropriate stage.\n \t\t */\n-\t\tif (update_file(o, 0, &dest->oid, dest->mode, alt_path))\n+\t\tif (update_file(opt, 0, &dest->oid, dest->mode, alt_path))\n \t\t\treturn -1;\n \t\tfree(alt_path);\n-\t\treturn update_stages(o, dest->path, NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n+\t\treturn update_stages(opt, dest->path, NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n-\tif (update_file(o, 1, &dest->oid, dest->mode, dest->path))\n+\tif (update_file(opt, 1, &dest->oid, dest->mode, dest->path))\n \t\treturn -1;\n \treturn 0;\n }\n \n-static int handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *opt,\n \t\t\t\tconst char *path, const char *old_path,\n \t\t\t\tconst struct object_id *o_oid, int o_mode,\n \t\t\t\tconst struct object_id *changed_oid,\n@@ -1450,20 +1450,20 @@ static int handle_change_delete(struct merge_options *o,\n \tconst char *update_path = path;\n \tint ret = 0;\n \n-\tif (dir_in_way(o->repo->index, path, !o->call_depth, 0) ||\n-\t    (!o->call_depth && would_lose_untracked(o, path))) {\n-\t\tupdate_path = alt_path = unique_path(o, path, change_branch);\n+\tif (dir_in_way(opt->repo->index, path, !opt->call_depth, 0) ||\n+\t    (!opt->call_depth && would_lose_untracked(opt, path))) {\n+\t\tupdate_path = alt_path = unique_path(opt, path, change_branch);\n \t}\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\t/*\n \t\t * We cannot arbitrarily accept either a_sha or b_sha as\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tret = remove_file_from_index(o->repo->index, path);\n+\t\tret = remove_file_from_index(opt->repo->index, path);\n \t\tif (!ret)\n-\t\t\tret = update_file(o, 0, o_oid, o_mode, update_path);\n+\t\t\tret = update_file(opt, 0, o_oid, o_mode, update_path);\n \t} else {\n \t\t/*\n \t\t * Despite the four nearly duplicate messages and argument\n@@ -1482,24 +1482,24 @@ static int handle_change_delete(struct merge_options *o,\n \t\t */\n \t\tif (!alt_path) {\n \t\t\tif (!old_path) {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t\t       change, path, delete_branch, change_past,\n \t\t\t\t       change_branch, change_branch, path);\n \t\t\t} else {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s to %s in %s. Version %s of %s left in tree.\"),\n \t\t\t\t       change, old_path, delete_branch, change_past, path,\n \t\t\t\t       change_branch, change_branch, path);\n \t\t\t}\n \t\t} else {\n \t\t\tif (!old_path) {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t\t       change, path, delete_branch, change_past,\n \t\t\t\t       change_branch, change_branch, path, alt_path);\n \t\t\t} else {\n-\t\t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n+\t\t\t\toutput(opt, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t\t       \"and %s to %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t\t       change, old_path, delete_branch, change_past, path,\n \t\t\t\t       change_branch, change_branch, path, alt_path);\n@@ -1507,19 +1507,19 @@ static int handle_change_delete(struct merge_options *o,\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when change_branch ==\n-\t\t * o->branch1 && !alt_path, since that would needlessly touch\n+\t\t * opt->branch1 && !alt_path, since that would needlessly touch\n \t\t * path.  We could call update_file_flags() with update_cache=0\n \t\t * and update_wd=0, but that's a no-op.\n \t\t */\n-\t\tif (change_branch != o->branch1 || alt_path)\n-\t\t\tret = update_file(o, 0, changed_oid, changed_mode, update_path);\n+\t\tif (change_branch != opt->branch1 || alt_path)\n+\t\t\tret = update_file(opt, 0, changed_oid, changed_mode, update_path);\n \t}\n \tfree(alt_path);\n \n \treturn ret;\n }\n \n-static int handle_rename_delete(struct merge_options *o,\n+static int handle_rename_delete(struct merge_options *opt,\n \t\t\t\tstruct diff_filepair *pair,\n \t\t\t\tconst char *rename_branch,\n \t\t\t\tconst char *delete_branch)\n@@ -1527,21 +1527,21 @@ static int handle_rename_delete(struct merge_options *o,\n \tconst struct diff_filespec *orig = pair->one;\n \tconst struct diff_filespec *dest = pair->two;\n \n-\tif (handle_change_delete(o,\n-\t\t\t\t o->call_depth ? orig->path : dest->path,\n-\t\t\t\t o->call_depth ? NULL : orig->path,\n+\tif (handle_change_delete(opt,\n+\t\t\t\t opt->call_depth ? orig->path : dest->path,\n+\t\t\t\t opt->call_depth ? NULL : orig->path,\n \t\t\t\t &orig->oid, orig->mode,\n \t\t\t\t &dest->oid, dest->mode,\n \t\t\t\t rename_branch, delete_branch,\n \t\t\t\t _(\"rename\"), _(\"renamed\")))\n \t\treturn -1;\n \n-\tif (o->call_depth)\n-\t\treturn remove_file_from_index(o->repo->index, dest->path);\n+\tif (opt->call_depth)\n+\t\treturn remove_file_from_index(opt->repo->index, dest->path);\n \telse\n-\t\treturn update_stages(o, dest->path, NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n+\t\treturn update_stages(opt, dest->path, NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1557,7 +1557,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static int handle_file_collision(struct merge_options *o,\n+static int handle_file_collision(struct merge_options *opt,\n \t\t\t\t const char *collide_path,\n \t\t\t\t const char *prev_path1,\n \t\t\t\t const char *prev_path2,\n@@ -1575,11 +1575,11 @@ static int handle_file_collision(struct merge_options *o,\n \t/*\n \t * It's easiest to get the correct things into stage 2 and 3, and\n \t * to make sure that the content merge puts HEAD before the other\n-\t * branch if we just ensure that branch1 == o->branch1.  So, simply\n+\t * branch if we just ensure that branch1 == opt->branch1.  So, simply\n \t * flip arguments around if we don't have that.\n \t */\n-\tif (branch1 != o->branch1) {\n-\t\treturn handle_file_collision(o, collide_path,\n+\tif (branch1 != opt->branch1) {\n+\t\treturn handle_file_collision(opt, collide_path,\n \t\t\t\t\t     prev_path2, prev_path1,\n \t\t\t\t\t     branch2, branch1,\n \t\t\t\t\t     b_oid, b_mode,\n@@ -1589,60 +1589,60 @@ static int handle_file_collision(struct merge_options *o,\n \t/*\n \t * In the recursive case, we just opt to undo renames\n \t */\n-\tif (o->call_depth && (prev_path1 || prev_path2)) {\n+\tif (opt->call_depth && (prev_path1 || prev_path2)) {\n \t\t/* Put first file (a_oid, a_mode) in its original spot */\n \t\tif (prev_path1) {\n-\t\t\tif (update_file(o, 1, a_oid, a_mode, prev_path1))\n+\t\t\tif (update_file(opt, 1, a_oid, a_mode, prev_path1))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(o, 1, a_oid, a_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, a_oid, a_mode, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\t/* Put second file (b_oid, b_mode) in its original spot */\n \t\tif (prev_path2) {\n-\t\t\tif (update_file(o, 1, b_oid, b_mode, prev_path2))\n+\t\t\tif (update_file(opt, 1, b_oid, b_mode, prev_path2))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(o, 1, b_oid, b_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, b_oid, b_mode, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\t/* Don't leave something at collision path if unrenaming both */\n \t\tif (prev_path1 && prev_path2)\n-\t\t\tremove_file(o, 1, collide_path, 0);\n+\t\t\tremove_file(opt, 1, collide_path, 0);\n \n \t\treturn 0;\n \t}\n \n \t/* Remove rename sources if rename/add or rename/rename(2to1) */\n \tif (prev_path1)\n-\t\tremove_file(o, 1, prev_path1,\n-\t\t\t    o->call_depth || would_lose_untracked(o, prev_path1));\n+\t\tremove_file(opt, 1, prev_path1,\n+\t\t\t    opt->call_depth || would_lose_untracked(opt, prev_path1));\n \tif (prev_path2)\n-\t\tremove_file(o, 1, prev_path2,\n-\t\t\t    o->call_depth || would_lose_untracked(o, prev_path2));\n+\t\tremove_file(opt, 1, prev_path2,\n+\t\t\t    opt->call_depth || would_lose_untracked(opt, prev_path2));\n \n \t/*\n \t * Remove the collision path, if it wouldn't cause dirty contents\n \t * or an untracked file to get lost.  We'll either overwrite with\n \t * merged contents, or just write out to differently named files.\n \t */\n-\tif (was_dirty(o, collide_path)) {\n-\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\tif (was_dirty(opt, collide_path)) {\n+\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t       collide_path);\n-\t\tupdate_path = alt_path = unique_path(o, collide_path, \"merged\");\n-\t} else if (would_lose_untracked(o, collide_path)) {\n+\t\tupdate_path = alt_path = unique_path(opt, collide_path, \"merged\");\n+\t} else if (would_lose_untracked(opt, collide_path)) {\n \t\t/*\n \t\t * Only way we get here is if both renames were from\n \t\t * a directory rename AND user had an untracked file\n \t\t * at the location where both files end up after the\n \t\t * two directory renames.  See testcase 10d of t6043.\n \t\t */\n-\t\toutput(o, 1, _(\"Refusing to lose untracked file at \"\n+\t\toutput(opt, 1, _(\"Refusing to lose untracked file at \"\n \t\t\t       \"%s, even though it's in the way.\"),\n \t\t       collide_path);\n-\t\tupdate_path = alt_path = unique_path(o, collide_path, \"merged\");\n+\t\tupdate_path = alt_path = unique_path(opt, collide_path, \"merged\");\n \t} else {\n \t\t/*\n \t\t * FIXME: It's possible that the two files are identical\n@@ -1654,7 +1654,7 @@ static int handle_file_collision(struct merge_options *o,\n \t\t * merge-recursive interoperate anyway, so punting for\n \t\t * now...\n \t\t */\n-\t\tremove_file(o, 0, collide_path, 0);\n+\t\tremove_file(opt, 0, collide_path, 0);\n \t}\n \n \t/* Store things in diff_filespecs for functions that need it */\n@@ -1670,14 +1670,14 @@ static int handle_file_collision(struct merge_options *o,\n \tb.mode = b_mode;\n \tb.oid_valid = 1;\n \n-\tif (merge_mode_and_contents(o, &null, &a, &b, collide_path,\n-\t\t\t\t    branch1, branch2, o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, &null, &a, &b, collide_path,\n+\t\t\t\t    branch1, branch2, opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tmfi.clean &= !alt_path;\n-\tif (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path))\n+\tif (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, update_path))\n \t\treturn -1;\n-\tif (!mfi.clean && !o->call_depth &&\n-\t    update_stages(o, collide_path, NULL, &a, &b))\n+\tif (!mfi.clean && !opt->call_depth &&\n+\t    update_stages(opt, collide_path, NULL, &a, &b))\n \t\treturn -1;\n \tfree(alt_path);\n \t/*\n@@ -1690,7 +1690,7 @@ static int handle_file_collision(struct merge_options *o,\n \treturn mfi.clean;\n }\n \n-static int handle_rename_add(struct merge_options *o,\n+static int handle_rename_add(struct merge_options *opt,\n \t\t\t     struct rename_conflict_info *ci)\n {\n \t/* a was renamed to c, and a separate c was added. */\n@@ -1700,21 +1700,21 @@ static int handle_rename_add(struct merge_options *o,\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n \n-\tint other_stage = (ci->branch1 == o->branch1 ? 3 : 2);\n+\tint other_stage = (ci->branch1 == opt->branch1 ? 3 : 2);\n \n-\toutput(o, 1, _(\"CONFLICT (rename/add): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/add): \"\n \t       \"Rename %s->%s in %s.  Added %s in %s\"),\n \t       a->path, c->path, ci->branch1,\n \t       c->path, ci->branch2);\n \n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, a, c, &ci->ren1_other, prev_path_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(prev_path_desc);\n \n-\treturn handle_file_collision(o,\n+\treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi.oid, mfi.mode,\n@@ -1722,20 +1722,20 @@ static int handle_rename_add(struct merge_options *o,\n \t\t\t\t     ci->dst_entry1->stages[other_stage].mode);\n }\n \n-static char *find_path_for_conflict(struct merge_options *o,\n+static char *find_path_for_conflict(struct merge_options *opt,\n \t\t\t\t    const char *path,\n \t\t\t\t    const char *branch1,\n \t\t\t\t    const char *branch2)\n {\n \tchar *new_path = NULL;\n-\tif (dir_in_way(o->repo->index, path, !o->call_depth, 0)) {\n-\t\tnew_path = unique_path(o, path, branch1);\n-\t\toutput(o, 1, _(\"%s is a directory in %s adding \"\n+\tif (dir_in_way(opt->repo->index, path, !opt->call_depth, 0)) {\n+\t\tnew_path = unique_path(opt, path, branch1);\n+\t\toutput(opt, 1, _(\"%s is a directory in %s adding \"\n \t\t\t       \"as %s instead\"),\n \t\t       path, branch2, new_path);\n-\t} else if (would_lose_untracked(o, path)) {\n-\t\tnew_path = unique_path(o, path, branch1);\n-\t\toutput(o, 1, _(\"Refusing to lose untracked file\"\n+\t} else if (would_lose_untracked(opt, path)) {\n+\t\tnew_path = unique_path(opt, path, branch1);\n+\t\toutput(opt, 1, _(\"Refusing to lose untracked file\"\n \t\t\t       \" at %s; adding as %s instead\"),\n \t\t       path, new_path);\n \t}\n@@ -1743,7 +1743,7 @@ static char *find_path_for_conflict(struct merge_options *o,\n \treturn new_path;\n }\n \n-static int handle_rename_rename_1to2(struct merge_options *o,\n+static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1755,29 +1755,29 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \tstruct diff_filespec *b = ci->pair2->two;\n \tchar *path_desc;\n \n-\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n \t       \"rename \\\"%s\\\"->\\\"%s\\\" in \\\"%s\\\"%s\"),\n \t       one->path, a->path, ci->branch1,\n \t       one->path, b->path, ci->branch2,\n-\t       o->call_depth ? _(\" (left unresolved)\") : \"\");\n+\t       opt->call_depth ? _(\" (left unresolved)\") : \"\");\n \n \tpath_desc = xstrfmt(\"%s and %s, both renamed from %s\",\n \t\t\t    a->path, b->path, one->path);\n-\tif (merge_mode_and_contents(o, one, a, b, path_desc,\n+\tif (merge_mode_and_contents(opt, one, a, b, path_desc,\n \t\t\t\t    ci->branch1, ci->branch2,\n-\t\t\t\t    o->call_depth * 2, &mfi))\n+\t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(path_desc);\n \n-\tif (o->call_depth) {\n+\tif (opt->call_depth) {\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, one->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -1790,18 +1790,18 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n \t\tif (add) {\n-\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n-\t\t\tremove_file_from_index(o->repo->index, a->path);\n+\t\t\tremove_file_from_index(opt->repo->index, a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n \t\tif (add) {\n-\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n-\t\t\tremove_file_from_index(o->repo->index, b->path);\n+\t\t\tremove_file_from_index(opt->repo->index, b->path);\n \t} else {\n \t\t/*\n \t\t * For each destination path, we need to see if there is a\n@@ -1810,39 +1810,39 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n \t\tif (add) {\n-\t\t\tif (handle_file_collision(o, a->path,\n+\t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->branch1, ci->branch2,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode,\n \t\t\t\t\t\t  &add->oid, add->mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tchar *new_path = find_path_for_conflict(o, a->path,\n+\t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n \t\t\t\t\t\t\t\tci->branch1,\n \t\t\t\t\t\t\t\tci->branch2);\n-\t\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n+\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n-\t\t\tif (update_stages(o, a->path, NULL, a, NULL))\n+\t\t\tif (update_stages(opt, a->path, NULL, a, NULL))\n \t\t\t\treturn -1;\n \t\t}\n \n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n \t\tif (add) {\n-\t\t\tif (handle_file_collision(o, b->path,\n+\t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->branch1, ci->branch2,\n \t\t\t\t\t\t  &add->oid, add->mode,\n \t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tchar *new_path = find_path_for_conflict(o, b->path,\n+\t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n \t\t\t\t\t\t\t\tci->branch2,\n \t\t\t\t\t\t\t\tci->branch1);\n-\t\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n+\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n-\t\t\tif (update_stages(o, b->path, NULL, NULL, b))\n+\t\t\tif (update_stages(opt, b->path, NULL, NULL, b))\n \t\t\t\treturn -1;\n \t\t}\n \t}\n@@ -1850,7 +1850,7 @@ static int handle_rename_rename_1to2(struct merge_options *o,\n \treturn 0;\n }\n \n-static int handle_rename_rename_2to1(struct merge_options *o,\n+static int handle_rename_rename_2to1(struct merge_options *opt,\n \t\t\t\t     struct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1864,7 +1864,7 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n \n-\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n \t       \"Rename %s->%s in %s\"),\n \t       a->path, c1->path, ci->branch1,\n@@ -1872,17 +1872,17 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n \n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    1 + o->call_depth * 2, &mfi_c2))\n+\tif (merge_mode_and_contents(opt, a, c1, &ci->ren1_other, path_side_1_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n+\t    merge_mode_and_contents(opt, b, &ci->ren2_other, c2, path_side_2_desc,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n \tfree(path_side_1_desc);\n \tfree(path_side_2_desc);\n \n-\treturn handle_file_collision(o, path, a->path, b->path,\n+\treturn handle_file_collision(opt, path, a->path, b->path,\n \t\t\t\t     ci->branch1, ci->branch2,\n \t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n \t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n@@ -1891,17 +1891,17 @@ static int handle_rename_rename_2to1(struct merge_options *o,\n /*\n  * Get the diff_filepairs changed between o_tree and tree.\n  */\n-static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n+static struct diff_queue_struct *get_diffpairs(struct merge_options *opt,\n \t\t\t\t\t       struct tree *o_tree,\n \t\t\t\t\t       struct tree *tree)\n {\n \tstruct diff_queue_struct *ret;\n \tstruct diff_options opts;\n \n-\trepo_diff_setup(o->repo, &opts);\n+\trepo_diff_setup(opt->repo, &opts);\n \topts.flags.recursive = 1;\n \topts.flags.rename_empty = 0;\n-\topts.detect_rename = merge_detect_rename(o);\n+\topts.detect_rename = merge_detect_rename(opt);\n \t/*\n \t * We do not have logic to handle the detection of copies.  In\n \t * fact, it may not even make sense to add such logic: would we\n@@ -1910,17 +1910,17 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n \t */\n \tif (opts.detect_rename > DIFF_DETECT_RENAME)\n \t\topts.detect_rename = DIFF_DETECT_RENAME;\n-\topts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :\n-\t\t\t    o->diff_rename_limit >= 0 ? o->diff_rename_limit :\n+\topts.rename_limit = opt->merge_rename_limit >= 0 ? opt->merge_rename_limit :\n+\t\t\t    opt->diff_rename_limit >= 0 ? opt->diff_rename_limit :\n \t\t\t    1000;\n-\topts.rename_score = o->rename_score;\n-\topts.show_rename_progress = o->show_rename_progress;\n+\topts.rename_score = opt->rename_score;\n+\topts.show_rename_progress = opt->show_rename_progress;\n \topts.output_format = DIFF_FORMAT_NO_OUTPUT;\n \tdiff_setup_done(&opts);\n \tdiff_tree_oid(&o_tree->object.oid, &tree->object.oid, \"\", &opts);\n \tdiffcore_std(&opts);\n-\tif (opts.needed_rename_limit > o->needed_rename_limit)\n-\t\to->needed_rename_limit = opts.needed_rename_limit;\n+\tif (opts.needed_rename_limit > opt->needed_rename_limit)\n+\t\topt->needed_rename_limit = opts.needed_rename_limit;\n \n \tret = xmalloc(sizeof(*ret));\n \t*ret = diff_queued_diff;\n@@ -2039,7 +2039,7 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,\n  * level conflicts for the renamed location.  If there is a rename and\n  * there are no conflicts, return the new name.  Otherwise, return NULL.\n  */\n-static char *handle_path_level_conflicts(struct merge_options *o,\n+static char *handle_path_level_conflicts(struct merge_options *opt,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t struct dir_rename_entry *entry,\n \t\t\t\t\t struct hashmap *collisions,\n@@ -2060,7 +2060,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\t/* This should only happen when entry->non_unique_new_dir set */\n \t\tif (!entry->non_unique_new_dir)\n \t\t\tBUG(\"entry->non_unqiue_dir not set and !new_path\");\n-\t\toutput(o, 1, _(\"CONFLICT (directory rename split): \"\n+\t\toutput(opt, 1, _(\"CONFLICT (directory rename split): \"\n \t\t\t       \"Unclear where to place %s because directory \"\n \t\t\t       \"%s was renamed to multiple other directories, \"\n \t\t\t       \"with no destination getting a majority of the \"\n@@ -2092,7 +2092,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\tcollision_ent->reported_already = 1;\n \t\tstrbuf_add_separated_string_list(&collision_paths, \", \",\n \t\t\t\t\t\t &collision_ent->source_files);\n-\t\toutput(o, 1, _(\"CONFLICT (implicit dir rename): Existing \"\n+\t\toutput(opt, 1, _(\"CONFLICT (implicit dir rename): Existing \"\n \t\t\t       \"file/dir at %s in the way of implicit \"\n \t\t\t       \"directory rename(s) putting the following \"\n \t\t\t       \"path(s) there: %s.\"),\n@@ -2102,7 +2102,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n \t\tcollision_ent->reported_already = 1;\n \t\tstrbuf_add_separated_string_list(&collision_paths, \", \",\n \t\t\t\t\t\t &collision_ent->source_files);\n-\t\toutput(o, 1, _(\"CONFLICT (implicit dir rename): Cannot map \"\n+\t\toutput(opt, 1, _(\"CONFLICT (implicit dir rename): Cannot map \"\n \t\t\t       \"more than one path to %s; implicit directory \"\n \t\t\t       \"renames tried to put these paths there: %s\"),\n \t\t       new_path, collision_paths.buf);\n@@ -2139,7 +2139,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,\n  *         causes conflicts for files within those merged directories, then\n  *         that should be detected at the individual path level.\n  */\n-static void handle_directory_level_conflicts(struct merge_options *o,\n+static void handle_directory_level_conflicts(struct merge_options *opt,\n \t\t\t\t\t     struct hashmap *dir_re_head,\n \t\t\t\t\t     struct tree *head,\n \t\t\t\t\t     struct hashmap *dir_re_merge,\n@@ -2194,11 +2194,11 @@ static void handle_directory_level_conflicts(struct merge_options *o,\n \t\t\t * know that head_ent->new_dir and merge_ent->new_dir\n \t\t\t * are different strings.\n \t\t\t */\n-\t\t\toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n+\t\t\toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t\t\t\t       \"Rename directory %s->%s in %s. \"\n \t\t\t\t       \"Rename directory %s->%s in %s\"),\n-\t\t\t       head_ent->dir, head_ent->new_dir.buf, o->branch1,\n-\t\t\t       head_ent->dir, merge_ent->new_dir.buf, o->branch2);\n+\t\t\t       head_ent->dir, head_ent->new_dir.buf, opt->branch1,\n+\t\t\t       head_ent->dir, merge_ent->new_dir.buf, opt->branch2);\n \t\t\tstring_list_append(&remove_from_head,\n \t\t\t\t\t   head_ent->dir)->util = head_ent;\n \t\t\tstrbuf_release(&head_ent->new_dir);\n@@ -2397,7 +2397,7 @@ static void compute_collisions(struct hashmap *collisions,\n \t}\n }\n \n-static char *check_for_directory_rename(struct merge_options *o,\n+static char *check_for_directory_rename(struct merge_options *opt,\n \t\t\t\t\tconst char *path,\n \t\t\t\t\tstruct tree *tree,\n \t\t\t\t\tstruct hashmap *dir_renames,\n@@ -2438,11 +2438,11 @@ static char *check_for_directory_rename(struct merge_options *o,\n \t */\n \toentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);\n \tif (oentry) {\n-\t\toutput(o, 1, _(\"WARNING: Avoiding applying %s -> %s rename \"\n+\t\toutput(opt, 1, _(\"WARNING: Avoiding applying %s -> %s rename \"\n \t\t\t       \"to %s, because %s itself was renamed.\"),\n \t\t       entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);\n \t} else {\n-\t\tnew_path = handle_path_level_conflicts(o, path, entry,\n+\t\tnew_path = handle_path_level_conflicts(opt, path, entry,\n \t\t\t\t\t\t       collisions, tree);\n \t\t*clean_merge &= (new_path != NULL);\n \t}\n@@ -2450,7 +2450,7 @@ static char *check_for_directory_rename(struct merge_options *o,\n \treturn new_path;\n }\n \n-static void apply_directory_rename_modifications(struct merge_options *o,\n+static void apply_directory_rename_modifications(struct merge_options *opt,\n \t\t\t\t\t\t struct diff_filepair *pair,\n \t\t\t\t\t\t char *new_path,\n \t\t\t\t\t\t struct rename *re,\n@@ -2473,11 +2473,11 @@ static void apply_directory_rename_modifications(struct merge_options *o,\n \t * saying the file would have been overwritten), but it might\n \t * be dirty, though.\n \t */\n-\tupdate_wd = !was_dirty(o, pair->two->path);\n+\tupdate_wd = !was_dirty(opt, pair->two->path);\n \tif (!update_wd)\n-\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t       pair->two->path);\n-\tremove_file(o, 1, pair->two->path, !update_wd);\n+\tremove_file(opt, 1, pair->two->path, !update_wd);\n \n \t/* Find or create a new re->dst_entry */\n \titem = string_list_lookup(entries, new_path);\n@@ -2566,7 +2566,7 @@ static void apply_directory_rename_modifications(struct merge_options *o,\n  * to be able to associate the correct cache entries with the rename\n  * information; tree is always equal to either a_tree or b_tree.\n  */\n-static struct string_list *get_renames(struct merge_options *o,\n+static struct string_list *get_renames(struct merge_options *opt,\n \t\t\t\t       struct diff_queue_struct *pairs,\n \t\t\t\t       struct hashmap *dir_renames,\n \t\t\t\t       struct hashmap *dir_rename_exclusions,\n@@ -2596,7 +2596,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \t\t\tdiff_free_filepair(pair);\n \t\t\tcontinue;\n \t\t}\n-\t\tnew_path = check_for_directory_rename(o, pair->two->path, tree,\n+\t\tnew_path = check_for_directory_rename(opt, pair->two->path, tree,\n \t\t\t\t\t\t      dir_renames,\n \t\t\t\t\t\t      dir_rename_exclusions,\n \t\t\t\t\t\t      &collisions,\n@@ -2626,7 +2626,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \t\titem = string_list_insert(renames, pair->one->path);\n \t\titem->util = re;\n \t\tif (new_path)\n-\t\t\tapply_directory_rename_modifications(o, pair, new_path,\n+\t\t\tapply_directory_rename_modifications(opt, pair, new_path,\n \t\t\t\t\t\t\t     re, tree, o_tree,\n \t\t\t\t\t\t\t     a_tree, b_tree,\n \t\t\t\t\t\t\t     entries);\n@@ -2641,7 +2641,7 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int process_renames(struct merge_options *o,\n+static int process_renames(struct merge_options *opt,\n \t\t\t   struct string_list *a_renames,\n \t\t\t   struct string_list *b_renames)\n {\n@@ -2685,13 +2685,13 @@ static int process_renames(struct merge_options *o,\n \t\tif (ren1) {\n \t\t\trenames1 = a_renames;\n \t\t\trenames2Dst = &b_by_dst;\n-\t\t\tbranch1 = o->branch1;\n-\t\t\tbranch2 = o->branch2;\n+\t\t\tbranch1 = opt->branch1;\n+\t\t\tbranch2 = opt->branch2;\n \t\t} else {\n \t\t\trenames1 = b_renames;\n \t\t\trenames2Dst = &a_by_dst;\n-\t\t\tbranch1 = o->branch2;\n-\t\t\tbranch2 = o->branch1;\n+\t\t\tbranch1 = opt->branch2;\n+\t\t\tbranch2 = opt->branch1;\n \t\t\tSWAP(ren2, ren1);\n \t\t}\n \n@@ -2725,7 +2725,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * the base stage (think of rename +\n \t\t\t\t * add-source cases).\n \t\t\t\t */\n-\t\t\t\tremove_file(o, 1, ren1_src, 1);\n+\t\t\t\tremove_file(opt, 1, ren1_src, 1);\n \t\t\t\tupdate_entry(ren1->dst_entry,\n \t\t\t\t\t     ren1->pair->one,\n \t\t\t\t\t     ren1->pair->two,\n@@ -2738,7 +2738,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t   NULL);\n \t\t} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {\n@@ -2765,7 +2765,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t   ren2->dst_entry,\n-\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t   ren2->src_entry);\n \n@@ -2788,8 +2788,8 @@ static int process_renames(struct merge_options *o,\n \t\t\t * stage and in other_stage (think of rename +\n \t\t\t * add-source case).\n \t\t\t */\n-\t\t\tremove_file(o, 1, ren1_src,\n-\t\t\t\t    renamed_stage == 2 || !was_tracked(o, ren1_src));\n+\t\t\tremove_file(opt, 1, ren1_src,\n+\t\t\t\t    renamed_stage == 2 || !was_tracked(opt, ren1_src));\n \n \t\t\toidcpy(&src_other.oid,\n \t\t\t       &ren1->src_entry->stages[other_stage].oid);\n@@ -2808,7 +2808,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if (oid_eq(&src_other.oid, &null_oid)) {\n@@ -2819,7 +2819,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n@@ -2832,7 +2832,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tif (update_file_flags(o,\n+\t\t\t\tif (update_file_flags(opt,\n \t\t\t\t\t\t      &ren1->pair->two->oid,\n \t\t\t\t\t\t      ren1->pair->two->mode,\n \t\t\t\t\t\t      ren1_dst,\n@@ -2854,7 +2854,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   branch2,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   ren1->src_entry,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t} else\n@@ -2882,7 +2882,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   ren1->dst_entry,\n \t\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t\t   o,\n+\t\t\t\t\t\t\t   opt,\n \t\t\t\t\t\t\t   NULL,\n \t\t\t\t\t\t\t   NULL);\n \t\t\t}\n@@ -2919,7 +2919,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,\n \tfree(pairs);\n }\n \n-static int detect_and_process_renames(struct merge_options *o,\n+static int detect_and_process_renames(struct merge_options *opt,\n \t\t\t\t      struct tree *common,\n \t\t\t\t      struct tree *head,\n \t\t\t\t      struct tree *merge,\n@@ -2933,17 +2933,17 @@ static int detect_and_process_renames(struct merge_options *o,\n \tri->head_renames = NULL;\n \tri->merge_renames = NULL;\n \n-\tif (!merge_detect_rename(o))\n+\tif (!merge_detect_rename(opt))\n \t\treturn 1;\n \n-\thead_pairs = get_diffpairs(o, common, head);\n-\tmerge_pairs = get_diffpairs(o, common, merge);\n+\thead_pairs = get_diffpairs(opt, common, head);\n+\tmerge_pairs = get_diffpairs(opt, common, merge);\n \n-\tif (o->detect_directory_renames) {\n+\tif (opt->detect_directory_renames) {\n \t\tdir_re_head = get_directory_renames(head_pairs);\n \t\tdir_re_merge = get_directory_renames(merge_pairs);\n \n-\t\thandle_directory_level_conflicts(o,\n+\t\thandle_directory_level_conflicts(opt,\n \t\t\t\t\t\t dir_re_head, head,\n \t\t\t\t\t\t dir_re_merge, merge);\n \t} else {\n@@ -2953,19 +2953,19 @@ static int detect_and_process_renames(struct merge_options *o,\n \t\tdir_rename_init(dir_re_merge);\n \t}\n \n-\tri->head_renames  = get_renames(o, head_pairs,\n+\tri->head_renames  = get_renames(opt, head_pairs,\n \t\t\t\t\tdir_re_merge, dir_re_head, head,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tri->merge_renames = get_renames(o, merge_pairs,\n+\tri->merge_renames = get_renames(opt, merge_pairs,\n \t\t\t\t\tdir_re_head, dir_re_merge, merge,\n \t\t\t\t\tcommon, head, merge, entries,\n \t\t\t\t\t&clean);\n \tif (clean < 0)\n \t\tgoto cleanup;\n-\tclean &= process_renames(o, ri->head_renames, ri->merge_renames);\n+\tclean &= process_renames(opt, ri->head_renames, ri->merge_renames);\n \n cleanup:\n \t/*\n@@ -3006,7 +3006,7 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(struct merge_options *o,\n+static int read_oid_strbuf(struct merge_options *opt,\n \t\t\t   const struct object_id *oid,\n \t\t\t   struct strbuf *dst)\n {\n@@ -3015,10 +3015,10 @@ static int read_oid_strbuf(struct merge_options *o,\n \tunsigned long size;\n \tbuf = read_object_file(oid, &type, &size);\n \tif (!buf)\n-\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(opt, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(opt, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n@@ -3060,7 +3060,7 @@ static int blob_unchanged(struct merge_options *opt,\n \treturn ret;\n }\n \n-static int handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n \t\t\t\tstruct object_id *a_oid, int a_mode,\n@@ -3071,18 +3071,18 @@ static int handle_modify_delete(struct merge_options *o,\n \tint changed_mode;\n \n \tif (a_oid) {\n-\t\tmodify_branch = o->branch1;\n-\t\tdelete_branch = o->branch2;\n+\t\tmodify_branch = opt->branch1;\n+\t\tdelete_branch = opt->branch2;\n \t\tchanged_oid = a_oid;\n \t\tchanged_mode = a_mode;\n \t} else {\n-\t\tmodify_branch = o->branch2;\n-\t\tdelete_branch = o->branch1;\n+\t\tmodify_branch = opt->branch2;\n+\t\tdelete_branch = opt->branch1;\n \t\tchanged_oid = b_oid;\n \t\tchanged_mode = b_mode;\n \t}\n \n-\treturn handle_change_delete(o,\n+\treturn handle_change_delete(opt,\n \t\t\t\t    path, NULL,\n \t\t\t\t    o_oid, o_mode,\n \t\t\t\t    changed_oid, changed_mode,\n@@ -3090,7 +3090,7 @@ static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n-static int handle_content_merge(struct merge_options *o,\n+static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n \t\t\t\tstruct object_id *o_oid, int o_mode,\n@@ -3119,26 +3119,26 @@ static int handle_content_merge(struct merge_options *o,\n \tif (rename_conflict_info) {\n \t\tstruct diff_filepair *pair1 = rename_conflict_info->pair1;\n \n-\t\tpath1 = (o->branch1 == rename_conflict_info->branch1) ?\n+\t\tpath1 = (opt->branch1 == rename_conflict_info->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\t/* If rename_conflict_info->pair2 != NULL, we are in\n \t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n \t\t * normal rename.\n \t\t */\n \t\tpath2 = (rename_conflict_info->pair2 ||\n-\t\t\t o->branch2 == rename_conflict_info->branch1) ?\n+\t\t\t opt->branch2 == rename_conflict_info->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \t\tone.path = pair1->one->path;\n \t\ta.path = (char *)path1;\n \t\tb.path = (char *)path2;\n \n-\t\tif (dir_in_way(o->repo->index, path, !o->call_depth,\n+\t\tif (dir_in_way(opt->repo->index, path, !opt->call_depth,\n \t\t\t       S_ISGITLINK(pair1->two->mode)))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tif (merge_mode_and_contents(o, &one, &a, &b, path,\n-\t\t\t\t    o->branch1, o->branch2,\n-\t\t\t\t    o->call_depth * 2, &mfi))\n+\tif (merge_mode_and_contents(opt, &one, &a, &b, path,\n+\t\t\t\t    opt->branch1, opt->branch2,\n+\t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \n \t/*\n@@ -3148,14 +3148,14 @@ static int handle_content_merge(struct merge_options *o,\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n \tif (mfi.clean &&\n-\t    was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&\n+\t    was_tracked_and_matches(opt, path, &mfi.oid, mfi.mode) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n-\t\toutput(o, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(o, mfi.mode, &mfi.oid, path,\n-\t\t\t\t  0, (!o->call_depth && !is_dirty), 0))\n+\t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n+\t\tif (add_cacheinfo(opt, mfi.mode, &mfi.oid, path,\n+\t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n \t\t * However, add_cacheinfo() will delete the old cache entry\n@@ -3163,11 +3163,11 @@ static int handle_content_merge(struct merge_options *o,\n \t\t * flag to avoid making the file appear as if it were\n \t\t * deleted by the user.\n \t\t */\n-\t\tpos = index_name_pos(&o->orig_index, path, strlen(path));\n-\t\tce = o->orig_index.cache[pos];\n+\t\tpos = index_name_pos(&opt->orig_index, path, strlen(path));\n+\t\tce = opt->orig_index.cache[pos];\n \t\tif (ce_skip_worktree(ce)) {\n-\t\t\tpos = index_name_pos(o->repo->index, path, strlen(path));\n-\t\t\tce = o->repo->index->cache[pos];\n+\t\t\tpos = index_name_pos(opt->repo->index, path, strlen(path));\n+\t\t\tce = opt->repo->index->cache[pos];\n \t\t\tce->ce_flags |= CE_SKIP_WORKTREE;\n \t\t}\n \t\treturn mfi.clean;\n@@ -3176,52 +3176,52 @@ static int handle_content_merge(struct merge_options *o,\n \tif (!mfi.clean) {\n \t\tif (S_ISGITLINK(mfi.mode))\n \t\t\treason = _(\"submodule\");\n-\t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n+\t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(o, path, &one, &a, &b))\n+\t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains || is_dirty) {\n \t\tchar *new_path;\n-\t\tif (o->call_depth) {\n-\t\t\tremove_file_from_index(o->repo->index, path);\n+\t\tif (opt->call_depth) {\n+\t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n+\t\t\t\tif (update_stages(opt, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n-\t\t\t\tint file_from_stage2 = was_tracked(o, path);\n+\t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(o, path, NULL,\n+\t\t\t\tif (update_stages(opt, path, NULL,\n \t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n-\t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n+\t\tnew_path = unique_path(opt, path, rename_conflict_info->branch1);\n \t\tif (is_dirty) {\n-\t\t\toutput(o, 1, _(\"Refusing to lose dirty file at %s\"),\n+\t\t\toutput(opt, 1, _(\"Refusing to lose dirty file at %s\"),\n \t\t\t       path);\n \t\t}\n-\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n+\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t} else if (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, path))\n \t\treturn -1;\n \treturn !is_dirty && mfi.clean;\n }\n \n-static int handle_rename_normal(struct merge_options *o,\n+static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tstruct object_id *o_oid, unsigned int o_mode,\n \t\t\t\tstruct object_id *a_oid, unsigned int a_mode,\n@@ -3229,17 +3229,17 @@ static int handle_rename_normal(struct merge_options *o,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n-\treturn handle_content_merge(o, path, was_dirty(o, path),\n+\treturn handle_content_merge(opt, path, was_dirty(opt, path),\n \t\t\t\t    o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,\n \t\t\t\t    ci);\n }\n \n /* Per entry merge function */\n-static int process_entry(struct merge_options *o,\n+static int process_entry(struct merge_options *opt,\n \t\t\t const char *path, struct stage_data *entry)\n {\n \tint clean_merge = 1;\n-\tint normalize = o->renormalize;\n+\tint normalize = opt->renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n \tunsigned b_mode = entry->stages[3].mode;\n@@ -3253,7 +3253,7 @@ static int process_entry(struct merge_options *o,\n \t\tswitch (conflict_info->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n-\t\t\tclean_merge = handle_rename_normal(o,\n+\t\t\tclean_merge = handle_rename_normal(opt,\n \t\t\t\t\t\t\t   path,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n@@ -3262,7 +3262,7 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(o,\n+\t\t\tif (handle_rename_via_dir(opt,\n \t\t\t\t\t\t  conflict_info->pair1,\n \t\t\t\t\t\t  conflict_info->branch1))\n \t\t\t\tclean_merge = -1;\n@@ -3274,11 +3274,11 @@ static int process_entry(struct merge_options *o,\n \t\t\t * two-way merged cleanly with the added file, I\n \t\t\t * guess it's a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_add(o, conflict_info);\n+\t\t\tclean_merge = handle_rename_add(opt, conflict_info);\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(o,\n+\t\t\tif (handle_rename_delete(opt,\n \t\t\t\t\t\t conflict_info->pair1,\n \t\t\t\t\t\t conflict_info->branch1,\n \t\t\t\t\t\t conflict_info->branch2))\n@@ -3286,7 +3286,7 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_rename_1to2(o, conflict_info))\n+\t\t\tif (handle_rename_rename_1to2(opt, conflict_info))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n@@ -3296,7 +3296,7 @@ static int process_entry(struct merge_options *o,\n \t\t\t * can then be two-way merged cleanly, I guess it's\n \t\t\t * a clean merge?\n \t\t\t */\n-\t\t\tclean_merge = handle_rename_rename_2to1(o,\n+\t\t\tclean_merge = handle_rename_rename_2to1(opt,\n \t\t\t\t\t\t\t\tconflict_info);\n \t\t\tbreak;\n \t\tdefault:\n@@ -3306,18 +3306,18 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(opt, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(opt, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n-\t\t\t\toutput(o, 2, _(\"Removing %s\"), path);\n+\t\t\t\toutput(opt, 2, _(\"Removing %s\"), path);\n \t\t\t/* do not touch working file if it did not exist */\n-\t\t\tremove_file(o, 1, path, !a_oid);\n+\t\t\tremove_file(opt, 1, path, !a_oid);\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\tif (handle_modify_delete(opt, path, o_oid, o_mode,\n \t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n \t\t\t\tclean_merge = -1;\n \t\t}\n@@ -3333,53 +3333,53 @@ static int process_entry(struct merge_options *o,\n \t\tconst char *conf;\n \n \t\tif (a_oid) {\n-\t\t\tadd_branch = o->branch1;\n-\t\t\tother_branch = o->branch2;\n+\t\t\tadd_branch = opt->branch1;\n+\t\t\tother_branch = opt->branch2;\n \t\t\tmode = a_mode;\n \t\t\toid = a_oid;\n \t\t\tconf = _(\"file/directory\");\n \t\t} else {\n-\t\t\tadd_branch = o->branch2;\n-\t\t\tother_branch = o->branch1;\n+\t\t\tadd_branch = opt->branch2;\n+\t\t\tother_branch = opt->branch1;\n \t\t\tmode = b_mode;\n \t\t\toid = b_oid;\n \t\t\tconf = _(\"directory/file\");\n \t\t}\n-\t\tif (dir_in_way(o->repo->index, path,\n-\t\t\t       !o->call_depth && !S_ISGITLINK(a_mode),\n+\t\tif (dir_in_way(opt->repo->index, path,\n+\t\t\t       !opt->call_depth && !S_ISGITLINK(a_mode),\n \t\t\t       0)) {\n-\t\t\tchar *new_path = unique_path(o, path, add_branch);\n+\t\t\tchar *new_path = unique_path(opt, path, add_branch);\n \t\t\tclean_merge = 0;\n-\t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n+\t\t\toutput(opt, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\tif (update_file(opt, 0, oid, mode, new_path))\n \t\t\t\tclean_merge = -1;\n-\t\t\telse if (o->call_depth)\n-\t\t\t\tremove_file_from_index(o->repo->index, path);\n+\t\t\telse if (opt->call_depth)\n+\t\t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t\tfree(new_path);\n \t\t} else {\n-\t\t\toutput(o, 2, _(\"Adding %s\"), path);\n+\t\t\toutput(opt, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\tif (update_file_flags(opt, oid, mode, path, 1, !a_oid))\n \t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\tif (!o_oid) {\n \t\t\t/* Case C: Added in both (check for same permissions) */\n-\t\t\toutput(o, 1,\n+\t\t\toutput(opt, 1,\n \t\t\t       _(\"CONFLICT (add/add): Merge conflict in %s\"),\n \t\t\t       path);\n-\t\t\tclean_merge = handle_file_collision(o,\n+\t\t\tclean_merge = handle_file_collision(opt,\n \t\t\t\t\t\t\t    path, NULL, NULL,\n-\t\t\t\t\t\t\t    o->branch1,\n-\t\t\t\t\t\t\t    o->branch2,\n+\t\t\t\t\t\t\t    opt->branch1,\n+\t\t\t\t\t\t\t    opt->branch2,\n \t\t\t\t\t\t\t    a_oid, a_mode,\n \t\t\t\t\t\t\t    b_oid, b_mode);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(o, path,\n+\t\t\tclean_merge = handle_content_merge(opt, path,\n \t\t\t\t\t\t\t   is_dirty,\n \t\t\t\t\t\t\t   o_oid, o_mode,\n \t\t\t\t\t\t\t   a_oid, a_mode,\n@@ -3391,48 +3391,48 @@ static int process_entry(struct merge_options *o,\n \t\t * this entry was deleted altogether. a_mode == 0 means\n \t\t * we had that path and want to actively remove it.\n \t\t */\n-\t\tremove_file(o, 1, path, !a_mode);\n+\t\tremove_file(opt, 1, path, !a_mode);\n \t} else\n \t\tBUG(\"fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n \n-int merge_trees(struct merge_options *o,\n+int merge_trees(struct merge_options *opt,\n \t\tstruct tree *head,\n \t\tstruct tree *merge,\n \t\tstruct tree *common,\n \t\tstruct tree **result)\n {\n-\tstruct index_state *istate = o->repo->index;\n+\tstruct index_state *istate = opt->repo->index;\n \tint code, clean;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (!o->call_depth && repo_index_has_changes(o->repo, head, &sb)) {\n-\t\terr(o, _(\"Your local changes to the following files would be overwritten by merge:\\n  %s\"),\n+\tif (!opt->call_depth && repo_index_has_changes(opt->repo, head, &sb)) {\n+\t\terr(opt, _(\"Your local changes to the following files would be overwritten by merge:\\n  %s\"),\n \t\t    sb.buf);\n \t\treturn -1;\n \t}\n \n-\tif (o->subtree_shift) {\n-\t\tmerge = shift_tree_object(o->repo, head, merge, o->subtree_shift);\n-\t\tcommon = shift_tree_object(o->repo, head, common, o->subtree_shift);\n+\tif (opt->subtree_shift) {\n+\t\tmerge = shift_tree_object(opt->repo, head, merge, opt->subtree_shift);\n+\t\tcommon = shift_tree_object(opt->repo, head, common, opt->subtree_shift);\n \t}\n \n \tif (oid_eq(&common->object.oid, &merge->object.oid)) {\n-\t\toutput(o, 0, _(\"Already up to date!\"));\n+\t\toutput(opt, 0, _(\"Already up to date!\"));\n \t\t*result = head;\n \t\treturn 1;\n \t}\n \n-\tcode = unpack_trees_start(o, common, head, merge);\n+\tcode = unpack_trees_start(opt, common, head, merge);\n \n \tif (code != 0) {\n-\t\tif (show(o, 4) || o->call_depth)\n-\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n+\t\tif (show(opt, 4) || opt->call_depth)\n+\t\t\terr(opt, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\tunpack_trees_finish(o);\n+\t\tunpack_trees_finish(opt);\n \t\treturn -1;\n \t}\n \n@@ -3447,21 +3447,21 @@ int merge_trees(struct merge_options *o,\n \t\t * opposed to decaring a local hashmap is for convenience\n \t\t * so that we don't have to pass it to around.\n \t\t */\n-\t\thashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);\n-\t\tget_files_dirs(o, head);\n-\t\tget_files_dirs(o, merge);\n+\t\thashmap_init(&opt->current_file_dir_set, path_hashmap_cmp, NULL, 512);\n+\t\tget_files_dirs(opt, head);\n+\t\tget_files_dirs(opt, merge);\n \n-\t\tentries = get_unmerged(o->repo->index);\n-\t\tclean = detect_and_process_renames(o, common, head, merge,\n+\t\tentries = get_unmerged(opt->repo->index);\n+\t\tclean = detect_and_process_renames(opt, common, head, merge,\n \t\t\t\t\t\t   entries, &re_info);\n-\t\trecord_df_conflict_files(o, entries);\n+\t\trecord_df_conflict_files(opt, entries);\n \t\tif (clean < 0)\n \t\t\tgoto cleanup;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed) {\n-\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tint ret = process_entry(opt, path, e);\n \t\t\t\tif (!ret)\n \t\t\t\t\tclean = 0;\n \t\t\t\telse if (ret < 0) {\n@@ -3483,19 +3483,19 @@ int merge_trees(struct merge_options *o,\n \t\tstring_list_clear(entries, 1);\n \t\tfree(entries);\n \n-\t\thashmap_free(&o->current_file_dir_set, 1);\n+\t\thashmap_free(&opt->current_file_dir_set, 1);\n \n \t\tif (clean < 0) {\n-\t\t\tunpack_trees_finish(o);\n+\t\t\tunpack_trees_finish(opt);\n \t\t\treturn clean;\n \t\t}\n \t}\n \telse\n \t\tclean = 1;\n \n-\tunpack_trees_finish(o);\n+\tunpack_trees_finish(opt);\n \n-\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\tif (opt->call_depth && !(*result = write_tree_from_memory(opt)))\n \t\treturn -1;\n \n \treturn clean;\n@@ -3516,7 +3516,7 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)\n  * Merge the commits h1 and h2, return the resulting virtual\n  * commit object and a flag indicating the cleanness of the merge.\n  */\n-int merge_recursive(struct merge_options *o,\n+int merge_recursive(struct merge_options *opt,\n \t\t    struct commit *h1,\n \t\t    struct commit *h2,\n \t\t    struct commit_list *ca,\n@@ -3527,10 +3527,10 @@ int merge_recursive(struct merge_options *o,\n \tstruct tree *mrtree;\n \tint clean;\n \n-\tif (show(o, 4)) {\n-\t\toutput(o, 4, _(\"Merging:\"));\n-\t\toutput_commit_title(o, h1);\n-\t\toutput_commit_title(o, h2);\n+\tif (show(opt, 4)) {\n+\t\toutput(opt, 4, _(\"Merging:\"));\n+\t\toutput_commit_title(opt, h1);\n+\t\toutput_commit_title(opt, h2);\n \t}\n \n \tif (!ca) {\n@@ -3538,13 +3538,13 @@ int merge_recursive(struct merge_options *o,\n \t\tca = reverse_commit_list(ca);\n \t}\n \n-\tif (show(o, 5)) {\n+\tif (show(opt, 5)) {\n \t\tunsigned cnt = commit_list_count(ca);\n \n-\t\toutput(o, 5, Q_(\"found %u common ancestor:\",\n+\t\toutput(opt, 5, Q_(\"found %u common ancestor:\",\n \t\t\t\t\"found %u common ancestors:\", cnt), cnt);\n \t\tfor (iter = ca; iter; iter = iter->next)\n-\t\t\toutput_commit_title(o, iter->item);\n+\t\t\toutput_commit_title(opt, iter->item);\n \t}\n \n \tmerged_common_ancestors = pop_commit(&ca);\n@@ -3552,13 +3552,13 @@ int merge_recursive(struct merge_options *o,\n \t\t/* if there is no common ancestor, use an empty tree */\n \t\tstruct tree *tree;\n \n-\t\ttree = lookup_tree(o->repo, o->repo->hash_algo->empty_tree);\n-\t\tmerged_common_ancestors = make_virtual_commit(o->repo, tree, \"ancestor\");\n+\t\ttree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);\n+\t\tmerged_common_ancestors = make_virtual_commit(opt->repo, tree, \"ancestor\");\n \t}\n \n \tfor (iter = ca; iter; iter = iter->next) {\n \t\tconst char *saved_b1, *saved_b2;\n-\t\to->call_depth++;\n+\t\topt->call_depth++;\n \t\t/*\n \t\t * When the merge fails, the result contains files\n \t\t * with conflict markers. The cleanness flag is\n@@ -3567,46 +3567,46 @@ int merge_recursive(struct merge_options *o,\n \t\t * overwritten it: the committed \"conflicts\" were\n \t\t * already resolved.\n \t\t */\n-\t\tdiscard_index(o->repo->index);\n-\t\tsaved_b1 = o->branch1;\n-\t\tsaved_b2 = o->branch2;\n-\t\to->branch1 = \"Temporary merge branch 1\";\n-\t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\tdiscard_index(opt->repo->index);\n+\t\tsaved_b1 = opt->branch1;\n+\t\tsaved_b2 = opt->branch2;\n+\t\topt->branch1 = \"Temporary merge branch 1\";\n+\t\topt->branch2 = \"Temporary merge branch 2\";\n+\t\tif (merge_recursive(opt, merged_common_ancestors, iter->item,\n \t\t\t\t    NULL, &merged_common_ancestors) < 0)\n \t\t\treturn -1;\n-\t\to->branch1 = saved_b1;\n-\t\to->branch2 = saved_b2;\n-\t\to->call_depth--;\n+\t\topt->branch1 = saved_b1;\n+\t\topt->branch2 = saved_b2;\n+\t\topt->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn err(o, _(\"merge returned no commit\"));\n+\t\t\treturn err(opt, _(\"merge returned no commit\"));\n \t}\n \n-\tdiscard_index(o->repo->index);\n-\tif (!o->call_depth)\n-\t\trepo_read_index(o->repo);\n+\tdiscard_index(opt->repo->index);\n+\tif (!opt->call_depth)\n+\t\trepo_read_index(opt->repo);\n \n-\to->ancestor = \"merged common ancestors\";\n-\tclean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),\n+\topt->ancestor = \"merged common ancestors\";\n+\tclean = merge_trees(opt, get_commit_tree(h1), get_commit_tree(h2),\n \t\t\t    get_commit_tree(merged_common_ancestors),\n \t\t\t    &mrtree);\n \tif (clean < 0) {\n-\t\tflush_output(o);\n+\t\tflush_output(opt);\n \t\treturn clean;\n \t}\n \n-\tif (o->call_depth) {\n-\t\t*result = make_virtual_commit(o->repo, mrtree, \"merged tree\");\n+\tif (opt->call_depth) {\n+\t\t*result = make_virtual_commit(opt->repo, mrtree, \"merged tree\");\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n-\tif (!o->call_depth && o->buffer_output < 2)\n-\t\tstrbuf_release(&o->obuf);\n-\tif (show(o, 2))\n+\tflush_output(opt);\n+\tif (!opt->call_depth && opt->buffer_output < 2)\n+\t\tstrbuf_release(&opt->obuf);\n+\tif (show(opt, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n-\t\t\t\t       o->needed_rename_limit, 0);\n+\t\t\t\t       opt->needed_rename_limit, 0);\n \treturn clean;\n }\n \n@@ -3628,7 +3628,7 @@ static struct commit *get_ref(struct repository *repo, const struct object_id *o\n \treturn (struct commit *)object;\n }\n \n-int merge_recursive_generic(struct merge_options *o,\n+int merge_recursive_generic(struct merge_options *opt,\n \t\t\t    const struct object_id *head,\n \t\t\t    const struct object_id *merge,\n \t\t\t    int num_base_list,\n@@ -3637,127 +3637,127 @@ int merge_recursive_generic(struct merge_options *o,\n {\n \tint clean;\n \tstruct lock_file lock = LOCK_INIT;\n-\tstruct commit *head_commit = get_ref(o->repo, head, o->branch1);\n-\tstruct commit *next_commit = get_ref(o->repo, merge, o->branch2);\n+\tstruct commit *head_commit = get_ref(opt->repo, head, opt->branch1);\n+\tstruct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);\n \tstruct commit_list *ca = NULL;\n \n \tif (base_list) {\n \t\tint i;\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n-\t\t\tif (!(base = get_ref(o->repo, base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n+\t\t\tif (!(base = get_ref(opt->repo, base_list[i], oid_to_hex(base_list[i]))))\n+\t\t\t\treturn err(opt, _(\"Could not parse object '%s'\"),\n \t\t\t\t\t   oid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n \t}\n \n-\trepo_hold_locked_index(o->repo, &lock, LOCK_DIE_ON_ERROR);\n-\tclean = merge_recursive(o, head_commit, next_commit, ca,\n+\trepo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);\n+\tclean = merge_recursive(opt, head_commit, next_commit, ca,\n \t\t\t\tresult);\n \tif (clean < 0) {\n \t\trollback_lock_file(&lock);\n \t\treturn clean;\n \t}\n \n-\tif (write_locked_index(o->repo->index, &lock,\n+\tif (write_locked_index(opt->repo->index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n-\t\treturn err(o, _(\"Unable to write index.\"));\n+\t\treturn err(opt, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n \n-static void merge_recursive_config(struct merge_options *o)\n+static void merge_recursive_config(struct merge_options *opt)\n {\n \tchar *value = NULL;\n-\tgit_config_get_int(\"merge.verbosity\", &o->verbosity);\n-\tgit_config_get_int(\"diff.renamelimit\", &o->diff_rename_limit);\n-\tgit_config_get_int(\"merge.renamelimit\", &o->merge_rename_limit);\n+\tgit_config_get_int(\"merge.verbosity\", &opt->verbosity);\n+\tgit_config_get_int(\"diff.renamelimit\", &opt->diff_rename_limit);\n+\tgit_config_get_int(\"merge.renamelimit\", &opt->merge_rename_limit);\n \tif (!git_config_get_string(\"diff.renames\", &value)) {\n-\t\to->diff_detect_rename = git_config_rename(\"diff.renames\", value);\n+\t\topt->diff_detect_rename = git_config_rename(\"diff.renames\", value);\n \t\tfree(value);\n \t}\n \tif (!git_config_get_string(\"merge.renames\", &value)) {\n-\t\to->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n+\t\topt->merge_detect_rename = git_config_rename(\"merge.renames\", value);\n \t\tfree(value);\n \t}\n \tgit_config(git_xmerge_config, NULL);\n }\n \n-void init_merge_options(struct merge_options *o,\n+void init_merge_options(struct merge_options *opt,\n \t\t\tstruct repository *repo)\n {\n \tconst char *merge_verbosity;\n-\tmemset(o, 0, sizeof(struct merge_options));\n-\to->repo = repo;\n-\to->verbosity = 2;\n-\to->buffer_output = 1;\n-\to->diff_rename_limit = -1;\n-\to->merge_rename_limit = -1;\n-\to->renormalize = 0;\n-\to->diff_detect_rename = -1;\n-\to->merge_detect_rename = -1;\n-\to->detect_directory_renames = 1;\n-\tmerge_recursive_config(o);\n+\tmemset(opt, 0, sizeof(struct merge_options));\n+\topt->repo = repo;\n+\topt->verbosity = 2;\n+\topt->buffer_output = 1;\n+\topt->diff_rename_limit = -1;\n+\topt->merge_rename_limit = -1;\n+\topt->renormalize = 0;\n+\topt->diff_detect_rename = -1;\n+\topt->merge_detect_rename = -1;\n+\topt->detect_directory_renames = 1;\n+\tmerge_recursive_config(opt);\n \tmerge_verbosity = getenv(\"GIT_MERGE_VERBOSITY\");\n \tif (merge_verbosity)\n-\t\to->verbosity = strtol(merge_verbosity, NULL, 10);\n-\tif (o->verbosity >= 5)\n-\t\to->buffer_output = 0;\n-\tstrbuf_init(&o->obuf, 0);\n-\tstring_list_init(&o->df_conflict_file_set, 1);\n+\t\topt->verbosity = strtol(merge_verbosity, NULL, 10);\n+\tif (opt->verbosity >= 5)\n+\t\topt->buffer_output = 0;\n+\tstrbuf_init(&opt->obuf, 0);\n+\tstring_list_init(&opt->df_conflict_file_set, 1);\n }\n \n-int parse_merge_opt(struct merge_options *o, const char *s)\n+int parse_merge_opt(struct merge_options *opt, const char *s)\n {\n \tconst char *arg;\n \n \tif (!s || !*s)\n \t\treturn -1;\n \tif (!strcmp(s, \"ours\"))\n-\t\to->recursive_variant = MERGE_RECURSIVE_OURS;\n+\t\topt->recursive_variant = MERGE_RECURSIVE_OURS;\n \telse if (!strcmp(s, \"theirs\"))\n-\t\to->recursive_variant = MERGE_RECURSIVE_THEIRS;\n+\t\topt->recursive_variant = MERGE_RECURSIVE_THEIRS;\n \telse if (!strcmp(s, \"subtree\"))\n-\t\to->subtree_shift = \"\";\n+\t\topt->subtree_shift = \"\";\n \telse if (skip_prefix(s, \"subtree=\", &arg))\n-\t\to->subtree_shift = arg;\n+\t\topt->subtree_shift = arg;\n \telse if (!strcmp(s, \"patience\"))\n-\t\to->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);\n+\t\topt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);\n \telse if (!strcmp(s, \"histogram\"))\n-\t\to->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);\n+\t\topt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);\n \telse if (skip_prefix(s, \"diff-algorithm=\", &arg)) {\n \t\tlong value = parse_algorithm_value(arg);\n \t\tif (value < 0)\n \t\t\treturn -1;\n \t\t/* clear out previous settings */\n-\t\tDIFF_XDL_CLR(o, NEED_MINIMAL);\n-\t\to->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;\n-\t\to->xdl_opts |= value;\n+\t\tDIFF_XDL_CLR(opt, NEED_MINIMAL);\n+\t\topt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;\n+\t\topt->xdl_opts |= value;\n \t}\n \telse if (!strcmp(s, \"ignore-space-change\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(s, \"ignore-all-space\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE);\n \telse if (!strcmp(s, \"ignore-space-at-eol\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);\n+\t\tDIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);\n \telse if (!strcmp(s, \"ignore-cr-at-eol\"))\n-\t\tDIFF_XDL_SET(o, IGNORE_CR_AT_EOL);\n+\t\tDIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);\n \telse if (!strcmp(s, \"renormalize\"))\n-\t\to->renormalize = 1;\n+\t\topt->renormalize = 1;\n \telse if (!strcmp(s, \"no-renormalize\"))\n-\t\to->renormalize = 0;\n+\t\topt->renormalize = 0;\n \telse if (!strcmp(s, \"no-renames\"))\n-\t\to->merge_detect_rename = 0;\n+\t\topt->merge_detect_rename = 0;\n \telse if (!strcmp(s, \"find-renames\")) {\n-\t\to->merge_detect_rename = 1;\n-\t\to->rename_score = 0;\n+\t\topt->merge_detect_rename = 1;\n+\t\topt->rename_score = 0;\n \t}\n \telse if (skip_prefix(s, \"find-renames=\", &arg) ||\n \t\t skip_prefix(s, \"rename-threshold=\", &arg)) {\n-\t\tif ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)\n+\t\tif ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)\n \t\t\treturn -1;\n-\t\to->merge_detect_rename = 1;\n+\t\topt->merge_detect_rename = 1;\n \t}\n \t/*\n \t * Please update $__git_merge_strategy_options in\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373216","messageId":"20190405150026.5260-11-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 10/15] merge-recursive: cleanup handle_rename_* function signatures","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:21Z","receivedAt":"2019-04-05T15:01:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Instead of passing various bits and pieces of 'ci', just pass it\ndirectly.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 31 +++++++++++++++----------------\n 1 file changed, 15 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f85c276f35..ada1c19ed2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1357,8 +1357,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n }\n \n static int handle_rename_via_dir(struct merge_options *opt,\n-\t\t\t\t struct diff_filepair *pair,\n-\t\t\t\t const char *rename_branch)\n+\t\t\t\t struct rename_conflict_info *ci)\n {\n \t/*\n \t * Handle file adds that need to be renamed due to directory rename\n@@ -1366,10 +1365,11 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t * there is no content merge to do; just move the file into the\n \t * desired final location.\n \t */\n-\tconst struct diff_filespec *dest = pair->two;\n+\tconst struct rename *ren = ci->ren1;\n+\tconst struct diff_filespec *dest = ren->pair->two;\n \n \tif (!opt->call_depth && would_lose_untracked(opt, dest->path)) {\n-\t\tchar *alt_path = unique_path(opt, dest->path, rename_branch);\n+\t\tchar *alt_path = unique_path(opt, dest->path, ren->branch);\n \n \t\toutput(opt, 1, _(\"Error: Refusing to lose untracked file at %s; \"\n \t\t\t       \"writing to %s instead.\"),\n@@ -1383,8 +1383,8 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t\t\treturn -1;\n \t\tfree(alt_path);\n \t\treturn update_stages(opt, dest->path, NULL,\n-\t\t\t\t     rename_branch == opt->branch1 ? dest : NULL,\n-\t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n+\t\t\t\t     ren->branch == opt->branch1 ? dest : NULL,\n+\t\t\t\t     ren->branch == opt->branch1 ? NULL : dest);\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n@@ -1476,12 +1476,14 @@ static int handle_change_delete(struct merge_options *opt,\n }\n \n static int handle_rename_delete(struct merge_options *opt,\n-\t\t\t\tstruct diff_filepair *pair,\n-\t\t\t\tconst char *rename_branch,\n-\t\t\t\tconst char *delete_branch)\n+\t\t\t\tstruct rename_conflict_info *ci)\n {\n-\tconst struct diff_filespec *orig = pair->one;\n-\tconst struct diff_filespec *dest = pair->two;\n+\tconst struct rename *ren = ci->ren1;\n+\tconst struct diff_filespec *orig = ren->pair->one;\n+\tconst struct diff_filespec *dest = ren->pair->two;\n+\tconst char *rename_branch = ren->branch;\n+\tconst char *delete_branch = (opt->branch1 == ren->branch ?\n+\t\t\t\t     opt->branch2 : opt->branch1);\n \n \tif (handle_change_delete(opt,\n \t\t\t\t opt->call_depth ? orig->path : dest->path,\n@@ -3184,8 +3186,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n \t\t\tclean_merge = 1;\n-\t\t\tif (handle_rename_via_dir(opt, ci->ren1->pair,\n-\t\t\t\t\t\t  ci->ren1->branch))\n+\t\t\tif (handle_rename_via_dir(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ADD:\n@@ -3199,9 +3200,7 @@ static int process_entry(struct merge_options *opt,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_rename_delete(opt, ci->ren1->pair,\n-\t\t\t\t\t\t ci->ren1->branch,\n-\t\t\t\t\t\t ci->ren1->branch == opt->branch1 ? opt->branch2 : opt->branch1))\n+\t\t\tif (handle_rename_delete(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373217","messageId":"20190405150026.5260-2-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 01/15] Use 'unsigned short' for mode, like diff_filespec does","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:12Z","receivedAt":"2019-04-05T15:01:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"struct diff_filespec defines mode to be an 'unsigned short'.  Several\nother places in the API which we'd like to interact with using a\ndiff_filespec used a plain unsigned (or unsigned int).  This caused\nproblems when taking addresses, so switch to unsigned short.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n archive.c              | 2 +-\n blame.c                | 2 +-\n blame.h                | 2 +-\n builtin/rm.c           | 2 +-\n builtin/update-index.c | 2 +-\n cache.h                | 2 +-\n fsck.c                 | 2 +-\n line-log.c             | 2 +-\n match-trees.c          | 8 ++++----\n merge-recursive.c      | 6 +++---\n notes.c                | 2 +-\n sha1-name.c            | 2 +-\n tree-diff.c            | 2 +-\n tree-walk.c            | 6 +++---\n tree-walk.h            | 6 +++---\n 15 files changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 1f98324a93..92bc001545 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -415,7 +415,7 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned int mode;\n+\t\tunsigned short mode;\n \t\tint err;\n \n \t\terr = get_tree_entry(&tree->object.oid, prefix, &tree_oid,\ndiff --git a/blame.c b/blame.c\nindex 5c07dec190..326231476e 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -99,7 +99,7 @@ static void verify_working_tree_path(struct repository *r,\n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\tif (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&\n \t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\ndiff --git a/blame.h b/blame.h\nindex be3a895043..2a285eb027 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -52,7 +52,7 @@ struct blame_origin {\n \tstruct blame_entry *suspects;\n \tmmfile_t file;\n \tstruct object_id blob_oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \t/* guilty gets set when shipping any suspects to the final\n \t * blame list instead of other commits\n \t */\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex db85b33982..90cbe896c9 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -110,7 +110,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\tconst struct cache_entry *ce;\n \t\tconst char *name = list.entry[i].name;\n \t\tstruct object_id oid;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tint local_changes = 0;\n \t\tint staged_changes = 0;\n \ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 1b6c42f748..0baf51c316 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -597,7 +597,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \ndiff --git a/cache.h b/cache.h\nindex ac92421f3a..851c3e6945 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1331,7 +1331,7 @@ static inline int hex2chr(const char *s)\n #define FALLBACK_DEFAULT_ABBREV 7\n \n struct object_context {\n-\tunsigned mode;\n+\tunsigned short mode;\n \t/*\n \t * symlink_path is only used by get_tree_entry_follow_symlinks,\n \t * and only for symlinks that point outside the repository.\ndiff --git a/fsck.c b/fsck.c\nindex 2260adb71e..4703f55561 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -604,7 +604,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \to_name = NULL;\n \n \twhile (desc.size) {\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tconst char *name;\n \t\tconst struct object_id *oid;\n \ndiff --git a/line-log.c b/line-log.c\nindex 24e21731c4..f743592bc0 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -498,7 +498,7 @@ static struct commit *check_single_commit(struct rev_info *revs)\n \n static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct object_id oid;\n \n \tif (get_tree_entry(&commit->object.oid, spec->path, &oid, &mode))\ndiff --git a/match-trees.c b/match-trees.c\nindex ddc4d39845..9d1ec8d6b0 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -140,7 +140,7 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tint score;\n \n \t\telem = tree_entry_extract(&one, &path, &mode);\n@@ -196,7 +196,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \trewrite_here = NULL;\n \twhile (desc.size) {\n \t\tconst char *name;\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n \t\tif (strlen(name) == toplen &&\n@@ -285,7 +285,7 @@ void shift_tree(const struct object_id *hash1,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \n \t\tif (!*del_prefix)\n \t\t\treturn;\n@@ -313,7 +313,7 @@ void shift_tree_by(const struct object_id *hash1,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned mode1, mode2;\n+\tunsigned short mode1, mode2;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6c40c61c47..fcf37be2a7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -214,7 +214,7 @@ struct rename_conflict_info {\n  */\n struct stage_data {\n \tstruct {\n-\t\tunsigned mode;\n+\t\tunsigned short mode;\n \t\tstruct object_id oid;\n \t} stages[4];\n \tstruct rename_conflict_info *rename_conflict_info;\n@@ -482,7 +482,7 @@ static void get_files_dirs(struct merge_options *o, struct tree *tree)\n static int get_tree_entry_if_blob(const struct object_id *tree,\n \t\t\t\t  const char *path,\n \t\t\t\t  struct object_id *hashy,\n-\t\t\t\t  unsigned int *mode_o)\n+\t\t\t\t  unsigned short *mode_o)\n {\n \tint ret;\n \n@@ -1935,7 +1935,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,\n static int tree_has_path(struct tree *tree, const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned int mode_o;\n+\tunsigned short mode_o;\n \n \treturn !get_tree_entry(&tree->object.oid, path,\n \t\t\t       &hashy, &mode_o);\ndiff --git a/notes.c b/notes.c\nindex 7f7cc4d511..ba4cae7851 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -986,7 +986,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\ndiff --git a/sha1-name.c b/sha1-name.c\nindex 6dda2c16df..d4b3d01f3c 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -1577,7 +1577,7 @@ static void diagnose_invalid_oid_path(const char *prefix,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned mode;\n+\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\ndiff --git a/tree-diff.c b/tree-diff.c\nindex e6d306f69f..f1f641eb6a 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -181,7 +181,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tstruct tree_desc *t, struct tree_desc *tp,\n \tint imin)\n {\n-\tunsigned mode;\n+\tunsigned short mode;\n \tconst char *path;\n \tconst struct object_id *oid;\n \tint pathlen;\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 1e4bbc8a0e..ec32a47b2e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -500,7 +500,7 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n-static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned *mode)\n+static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -535,7 +535,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_\n \treturn -1;\n }\n \n-int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned *mode)\n+int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\n@@ -585,7 +585,7 @@ int get_tree_entry(const struct object_id *tree_oid, const char *name, struct ob\n  * See the code for enum get_oid_result for a description of\n  * the return values.\n  */\n-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)\n+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode)\n {\n \tint retval = MISSING_OBJECT;\n \tstruct dir_state *parents = NULL;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 8225171866..3aa381b6a3 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -16,7 +16,7 @@ struct tree_desc {\n \tunsigned int size;\n };\n \n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n@@ -51,7 +51,7 @@ struct traverse_info;\n typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);\n int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);\n \n-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);\n+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);\n \n struct traverse_info {\n \tconst char *traverse_path;\n@@ -66,7 +66,7 @@ struct traverse_info {\n \tint show_all_errors;\n };\n \n-int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned *);\n+int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);\n extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);\n extern void setup_traverse_info(struct traverse_info *info, const char *base);\n \n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373218","messageId":"20190405150026.5260-12-newren@gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"[PATCH v3 11/15] merge-recursive: switch from (oid,mode) pairs to a diff_filespec","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2019-04-05T15:00:22Z","receivedAt":"2019-04-05T15:01:19Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"There was a significant inconsistency in the various parts of the API\nused in merge-recursive; many places used a pair of (oid, mode) to track\nfile version/contents, while other parts used a diff_filespec (which\nhave an oid and mode embedded in it).  This inconsistency caused lots of\nplaces to need to pack and unpack data to call into other functions.\nThis has been the subject of some past cleanups (see e.g. commit\n0270a07ad0b2 (\"merge-recursive: remove final remaining caller of\nmerge_file_one()\", 2018-09-19)), but let's just remove the underlying\nmess altogether by switching to use diff_filespec.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 483 +++++++++++++++++++++-------------------------\n 1 file changed, 215 insertions(+), 268 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ada1c19ed2..1d2c9e1772 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -201,10 +201,7 @@ enum rename_type {\n  * for these (temporary) data.\n  */\n struct stage_data {\n-\tstruct {\n-\t\tunsigned short mode;\n-\t\tstruct object_id oid;\n-\t} stages[4];\n+\tstruct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */\n \tstruct rename_conflict_info *rename_conflict_info;\n \tunsigned processed:1;\n };\n@@ -269,7 +266,6 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,\n \n \tci->ren1->dst_entry->processed = 0;\n \tci->ren1->dst_entry->rename_conflict_info = ci;\n-\n \tif (ren2) {\n \t\tci->ren2->dst_entry->rename_conflict_info = ci;\n \t}\n@@ -326,14 +322,14 @@ static void output_commit_title(struct merge_options *opt, struct commit *commit\n }\n \n static int add_cacheinfo(struct merge_options *opt,\n-\t\t\t unsigned int mode, const struct object_id *oid,\n+\t\t\t const struct diff_filespec *blob,\n \t\t\t const char *path, int stage, int refresh, int options)\n {\n \tstruct index_state *istate = opt->repo->index;\n \tstruct cache_entry *ce;\n \tint ret;\n \n-\tce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);\n+\tce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0);\n \tif (!ce)\n \t\treturn err(opt, _(\"add_cacheinfo failed for path '%s'; merge aborting.\"), path);\n \n@@ -464,15 +460,14 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n \n static int get_tree_entry_if_blob(const struct object_id *tree,\n \t\t\t\t  const char *path,\n-\t\t\t\t  struct object_id *hashy,\n-\t\t\t\t  unsigned short *mode_o)\n+\t\t\t\t  struct diff_filespec *dfs)\n {\n \tint ret;\n \n-\tret = get_tree_entry(tree, path, hashy, mode_o);\n-\tif (S_ISDIR(*mode_o)) {\n-\t\toidcpy(hashy, &null_oid);\n-\t\t*mode_o = 0;\n+\tret = get_tree_entry(tree, path, &dfs->oid, &dfs->mode);\n+\tif (S_ISDIR(dfs->mode)) {\n+\t\toidcpy(&dfs->oid, &null_oid);\n+\t\tdfs->mode = 0;\n \t}\n \treturn ret;\n }\n@@ -487,12 +482,9 @@ static struct stage_data *insert_stage_data(const char *path,\n {\n \tstruct string_list_item *item;\n \tstruct stage_data *e = xcalloc(1, sizeof(struct stage_data));\n-\tget_tree_entry_if_blob(&o->object.oid, path,\n-\t\t\t       &e->stages[1].oid, &e->stages[1].mode);\n-\tget_tree_entry_if_blob(&a->object.oid, path,\n-\t\t\t       &e->stages[2].oid, &e->stages[2].mode);\n-\tget_tree_entry_if_blob(&b->object.oid, path,\n-\t\t\t       &e->stages[3].oid, &e->stages[3].mode);\n+\tget_tree_entry_if_blob(&o->object.oid, path, &e->stages[1]);\n+\tget_tree_entry_if_blob(&a->object.oid, path, &e->stages[2]);\n+\tget_tree_entry_if_blob(&b->object.oid, path, &e->stages[3]);\n \titem = string_list_insert(entries, path);\n \titem->util = e;\n \treturn e;\n@@ -648,13 +640,13 @@ static int update_stages(struct merge_options *opt, const char *path,\n \t\tif (remove_file_from_index(opt->repo->index, path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -767,7 +759,7 @@ static int dir_in_way(struct index_state *istate, const char *path,\n  * and its oid and mode match the specified values\n  */\n static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n-\t\t\t\t   const struct object_id *oid, unsigned mode)\n+\t\t\t\t   const struct diff_filespec *blob)\n {\n \tint pos = index_name_pos(&opt->orig_index, path, strlen(path));\n \tstruct cache_entry *ce;\n@@ -778,7 +770,7 @@ static int was_tracked_and_matches(struct merge_options *opt, const char *path,\n \n \t/* See if the file we were tracking before matches */\n \tce = opt->orig_index.cache[pos];\n-\treturn (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);\n+\treturn (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);\n }\n \n /*\n@@ -903,8 +895,7 @@ static int make_room_for_path(struct merge_options *opt, const char *path)\n }\n \n static int update_file_flags(struct merge_options *opt,\n-\t\t\t     const struct object_id *oid,\n-\t\t\t     unsigned mode,\n+\t\t\t     const struct diff_filespec *contents,\n \t\t\t     const char *path,\n \t\t\t     int update_cache,\n \t\t\t     int update_wd)\n@@ -919,7 +910,7 @@ static int update_file_flags(struct merge_options *opt,\n \t\tvoid *buf;\n \t\tunsigned long size;\n \n-\t\tif (S_ISGITLINK(mode)) {\n+\t\tif (S_ISGITLINK(contents->mode)) {\n \t\t\t/*\n \t\t\t * We may later decide to recursively descend into\n \t\t\t * the submodule directory and update its index\n@@ -929,14 +920,16 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\tgoto update_index;\n \t\t}\n \n-\t\tbuf = read_object_file(oid, &type, &size);\n+\t\tbuf = read_object_file(&contents->oid, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(opt, _(\"cannot read object %s '%s'\"),\n+\t\t\t\t   oid_to_hex(&contents->oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(opt, _(\"blob expected for %s '%s'\"),\n+\t\t\t\t  oid_to_hex(&contents->oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n-\t\tif (S_ISREG(mode)) {\n+\t\tif (S_ISREG(contents->mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {\n \t\t\t\tfree(buf);\n@@ -949,12 +942,11 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\tupdate_wd = 0;\n \t\t\tgoto free_buf;\n \t\t}\n-\t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n+\t\tif (S_ISREG(contents->mode) ||\n+\t\t    (!has_symlinks && S_ISLNK(contents->mode))) {\n \t\t\tint fd;\n-\t\t\tif (mode & 0100)\n-\t\t\t\tmode = 0777;\n-\t\t\telse\n-\t\t\t\tmode = 0666;\n+\t\t\tint mode = (contents->mode & 0100 ? 0777 : 0666);\n+\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n \t\t\t\tret = err(opt, _(\"failed to open '%s': %s\"),\n@@ -963,7 +955,7 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n-\t\t} else if (S_ISLNK(mode)) {\n+\t\t} else if (S_ISLNK(contents->mode)) {\n \t\t\tchar *lnk = xmemdupz(buf, size);\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n@@ -974,13 +966,13 @@ static int update_file_flags(struct merge_options *opt,\n \t\t} else\n \t\t\tret = err(opt,\n \t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t\t  mode, oid_to_hex(oid), path);\n+\t\t\t\t  contents->mode, oid_to_hex(&contents->oid), path);\n \tfree_buf:\n \t\tfree(buf);\n \t}\n update_index:\n \tif (!ret && update_cache)\n-\t\tif (add_cacheinfo(opt, mode, oid, path, 0, update_wd,\n+\t\tif (add_cacheinfo(opt, contents, path, 0, update_wd,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD))\n \t\t\treturn -1;\n \treturn ret;\n@@ -988,18 +980,17 @@ static int update_file_flags(struct merge_options *opt,\n \n static int update_file(struct merge_options *opt,\n \t\t       int clean,\n-\t\t       const struct object_id *oid,\n-\t\t       unsigned mode,\n+\t\t       const struct diff_filespec *contents,\n \t\t       const char *path)\n {\n-\treturn update_file_flags(opt, oid, mode, path, opt->call_depth || clean, !opt->call_depth);\n+\treturn update_file_flags(opt, contents, path,\n+\t\t\t\t opt->call_depth || clean, !opt->call_depth);\n }\n \n /* Low level file merging, update and removal */\n \n struct merge_file_info {\n-\tstruct object_id oid;\n-\tunsigned mode;\n+\tstruct diff_filespec blob; /* mostly use oid & mode; sometimes path */\n \tunsigned clean:1,\n \t\t merge:1;\n };\n@@ -1039,6 +1030,7 @@ static int merge_3way(struct merge_options *opt,\n \t\t}\n \t}\n \n+\tassert(a->path && b->path);\n \tif (strcmp(a->path, b->path) ||\n \t    (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {\n \t\tbase_name = opt->ancestor == NULL ? NULL :\n@@ -1140,6 +1132,11 @@ static void print_commit(struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n+static int is_valid(const struct diff_filespec *dfs)\n+{\n+\treturn dfs->mode != 0 && !is_null_oid(&dfs->oid);\n+}\n+\n static int merge_submodule(struct merge_options *opt,\n \t\t\t   struct object_id *result, const char *path,\n \t\t\t   const struct object_id *base, const struct object_id *a,\n@@ -1278,11 +1275,11 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n \t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult->mode = a->mode;\n-\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\tresult->blob.mode = a->mode;\n+\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t} else {\n-\t\t\tresult->mode = b->mode;\n-\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\tresult->blob.mode = b->mode;\n+\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))\n@@ -1292,9 +1289,9 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == o->mode)\n-\t\t\tresult->mode = b->mode;\n+\t\t\tresult->blob.mode = b->mode;\n \t\telse {\n-\t\t\tresult->mode = a->mode;\n+\t\t\tresult->blob.mode = a->mode;\n \t\t\tif (b->mode != o->mode) {\n \t\t\t\tresult->clean = 0;\n \t\t\t\tresult->merge = 1;\n@@ -1302,9 +1299,9 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))\n-\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &o->oid))\n-\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint ret = 0, merge_status;\n@@ -1318,7 +1315,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \n \t\t\tif (!ret &&\n \t\t\t    write_object_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t      blob_type, &result->oid))\n+\t\t\t\t\t      blob_type, &result->blob.oid))\n \t\t\t\tret = err(opt, _(\"Unable to add %s to database\"),\n \t\t\t\t\t  a->path);\n \n@@ -1327,7 +1324,7 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult->clean = merge_submodule(opt, &result->oid,\n+\t\t\tresult->clean = merge_submodule(opt, &result->blob.oid,\n \t\t\t\t\t\t\to->path,\n \t\t\t\t\t\t\t&o->oid,\n \t\t\t\t\t\t\t&a->oid,\n@@ -1335,15 +1332,15 @@ static int merge_mode_and_contents(struct merge_options *opt,\n \t\t} else if (S_ISLNK(a->mode)) {\n \t\t\tswitch (opt->recursive_variant) {\n \t\t\tcase MERGE_RECURSIVE_NORMAL:\n-\t\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\t\tresult->clean = 0;\n \t\t\t\tbreak;\n \t\t\tcase MERGE_RECURSIVE_OURS:\n-\t\t\t\toidcpy(&result->oid, &a->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &a->oid);\n \t\t\t\tbreak;\n \t\t\tcase MERGE_RECURSIVE_THEIRS:\n-\t\t\t\toidcpy(&result->oid, &b->oid);\n+\t\t\t\toidcpy(&result->blob.oid, &b->oid);\n \t\t\t\tbreak;\n \t\t\t}\n \t\t} else\n@@ -1379,7 +1376,7 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t\t * index.  Instead, write to dest->path for the index but\n \t\t * only at the higher appropriate stage.\n \t\t */\n-\t\tif (update_file(opt, 0, &dest->oid, dest->mode, alt_path))\n+\t\tif (update_file(opt, 0, dest, alt_path))\n \t\t\treturn -1;\n \t\tfree(alt_path);\n \t\treturn update_stages(opt, dest->path, NULL,\n@@ -1388,16 +1385,15 @@ static int handle_rename_via_dir(struct merge_options *opt,\n \t}\n \n \t/* Update dest->path both in index and in worktree */\n-\tif (update_file(opt, 1, &dest->oid, dest->mode, dest->path))\n+\tif (update_file(opt, 1, dest, dest->path))\n \t\treturn -1;\n \treturn 0;\n }\n \n static int handle_change_delete(struct merge_options *opt,\n \t\t\t\tconst char *path, const char *old_path,\n-\t\t\t\tconst struct object_id *o_oid, int o_mode,\n-\t\t\t\tconst struct object_id *changed_oid,\n-\t\t\t\tint changed_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *changed,\n \t\t\t\tconst char *change_branch,\n \t\t\t\tconst char *delete_branch,\n \t\t\t\tconst char *change, const char *change_past)\n@@ -1419,7 +1415,7 @@ static int handle_change_delete(struct merge_options *opt,\n \t\t */\n \t\tret = remove_file_from_index(opt->repo->index, path);\n \t\tif (!ret)\n-\t\t\tret = update_file(opt, 0, o_oid, o_mode, update_path);\n+\t\t\tret = update_file(opt, 0, o, update_path);\n \t} else {\n \t\t/*\n \t\t * Despite the four nearly duplicate messages and argument\n@@ -1468,7 +1464,7 @@ static int handle_change_delete(struct merge_options *opt,\n \t\t * and update_wd=0, but that's a no-op.\n \t\t */\n \t\tif (change_branch != opt->branch1 || alt_path)\n-\t\t\tret = update_file(opt, 0, changed_oid, changed_mode, update_path);\n+\t\t\tret = update_file(opt, 0, changed, update_path);\n \t}\n \tfree(alt_path);\n \n@@ -1488,8 +1484,7 @@ static int handle_rename_delete(struct merge_options *opt,\n \tif (handle_change_delete(opt,\n \t\t\t\t opt->call_depth ? orig->path : dest->path,\n \t\t\t\t opt->call_depth ? NULL : orig->path,\n-\t\t\t\t &orig->oid, orig->mode,\n-\t\t\t\t &dest->oid, dest->mode,\n+\t\t\t\t orig, dest,\n \t\t\t\t rename_branch, delete_branch,\n \t\t\t\t _(\"rename\"), _(\"renamed\")))\n \t\treturn -1;\n@@ -1502,31 +1497,16 @@ static int handle_rename_delete(struct merge_options *opt,\n \t\t\t\t     rename_branch == opt->branch1 ? NULL : dest);\n }\n \n-static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n-\t\t\t\t\t\t struct stage_data *entry,\n-\t\t\t\t\t\t int stage)\n-{\n-\tstruct object_id *oid = &entry->stages[stage].oid;\n-\tunsigned mode = entry->stages[stage].mode;\n-\tif (mode == 0 || is_null_oid(oid))\n-\t\treturn NULL;\n-\toidcpy(&target->oid, oid);\n-\ttarget->mode = mode;\n-\treturn target;\n-}\n-\n static int handle_file_collision(struct merge_options *opt,\n \t\t\t\t const char *collide_path,\n \t\t\t\t const char *prev_path1,\n \t\t\t\t const char *prev_path2,\n \t\t\t\t const char *branch1, const char *branch2,\n-\t\t\t\t const struct object_id *a_oid,\n-\t\t\t\t unsigned int a_mode,\n-\t\t\t\t const struct object_id *b_oid,\n-\t\t\t\t unsigned int b_mode)\n+\t\t\t\t struct diff_filespec *a,\n+\t\t\t\t struct diff_filespec *b)\n {\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec null, a, b;\n+\tstruct diff_filespec null;\n \tchar *alt_path = NULL;\n \tconst char *update_path = collide_path;\n \n@@ -1540,29 +1520,28 @@ static int handle_file_collision(struct merge_options *opt,\n \t\treturn handle_file_collision(opt, collide_path,\n \t\t\t\t\t     prev_path2, prev_path1,\n \t\t\t\t\t     branch2, branch1,\n-\t\t\t\t\t     b_oid, b_mode,\n-\t\t\t\t\t     a_oid, a_mode);\n+\t\t\t\t\t     b, a);\n \t}\n \n \t/*\n \t * In the recursive case, we just opt to undo renames\n \t */\n \tif (opt->call_depth && (prev_path1 || prev_path2)) {\n-\t\t/* Put first file (a_oid, a_mode) in its original spot */\n+\t\t/* Put first file (a->oid, a->mode) in its original spot */\n \t\tif (prev_path1) {\n-\t\t\tif (update_file(opt, 1, a_oid, a_mode, prev_path1))\n+\t\t\tif (update_file(opt, 1, a, prev_path1))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(opt, 1, a_oid, a_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, a, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\t/* Put second file (b_oid, b_mode) in its original spot */\n+\t\t/* Put second file (b->oid, b->mode) in its original spot */\n \t\tif (prev_path2) {\n-\t\t\tif (update_file(opt, 1, b_oid, b_mode, prev_path2))\n+\t\t\tif (update_file(opt, 1, b, prev_path2))\n \t\t\t\treturn -1;\n \t\t} else {\n-\t\t\tif (update_file(opt, 1, b_oid, b_mode, collide_path))\n+\t\t\tif (update_file(opt, 1, b, collide_path))\n \t\t\t\treturn -1;\n \t\t}\n \n@@ -1616,26 +1595,18 @@ static int handle_file_collision(struct merge_options *opt,\n \t}\n \n \t/* Store things in diff_filespecs for functions that need it */\n-\tmemset(&a, 0, sizeof(struct diff_filespec));\n-\tmemset(&b, 0, sizeof(struct diff_filespec));\n-\tnull.path = a.path = b.path = (char *)collide_path;\n+\tnull.path = (char *)collide_path;\n \toidcpy(&null.oid, &null_oid);\n \tnull.mode = 0;\n-\toidcpy(&a.oid, a_oid);\n-\ta.mode = a_mode;\n-\ta.oid_valid = 1;\n-\toidcpy(&b.oid, b_oid);\n-\tb.mode = b_mode;\n-\tb.oid_valid = 1;\n-\n-\tif (merge_mode_and_contents(opt, &null, &a, &b, collide_path,\n+\n+\tif (merge_mode_and_contents(opt, &null, a, b, collide_path,\n \t\t\t\t    branch1, branch2, opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tmfi.clean &= !alt_path;\n-\tif (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, update_path))\n+\tif (update_file(opt, mfi.clean, &mfi.blob, update_path))\n \t\treturn -1;\n \tif (!mfi.clean && !opt->call_depth &&\n-\t    update_stages(opt, collide_path, NULL, &a, &b))\n+\t    update_stages(opt, collide_path, NULL, a, b))\n \t\treturn -1;\n \tfree(alt_path);\n \t/*\n@@ -1654,7 +1625,6 @@ static int handle_rename_add(struct merge_options *opt,\n \t/* a was renamed to c, and a separate c was added. */\n \tstruct diff_filespec *a = ci->ren1->pair->one;\n \tstruct diff_filespec *c = ci->ren1->pair->two;\n-\tstruct diff_filespec tmp;\n \tchar *path = c->path;\n \tchar *prev_path_desc;\n \tstruct merge_file_info mfi;\n@@ -1669,23 +1639,21 @@ static int handle_rename_add(struct merge_options *opt,\n \t       a->path, c->path, rename_branch,\n \t       c->path, add_branch);\n \n-\tfilespec_from_entry(&tmp, ci->ren1->src_entry, other_stage);\n-\ttmp.path = a->path;\n-\n \tprev_path_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n-\tif (merge_mode_and_contents(opt, a, c, &tmp,\n+\tif (merge_mode_and_contents(opt, a, c,\n+\t\t\t\t    &ci->ren1->src_entry->stages[other_stage],\n \t\t\t\t    prev_path_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi))\n \t\treturn -1;\n \tfree(prev_path_desc);\n \n+\tci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path;\n \treturn handle_file_collision(opt,\n \t\t\t\t     c->path, a->path, NULL,\n \t\t\t\t     rename_branch, add_branch,\n-\t\t\t\t     &mfi.oid, mfi.mode,\n-\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage].oid,\n-\t\t\t\t     ci->ren1->dst_entry->stages[other_stage].mode);\n+\t\t\t\t     &mfi.blob,\n+\t\t\t\t     &ci->ren1->dst_entry->stages[other_stage]);\n }\n \n static char *find_path_for_conflict(struct merge_options *opt,\n@@ -1714,7 +1682,6 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n {\n \t/* One file was renamed in both branches, but to different names. */\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n \tstruct diff_filespec *o = ci->ren1->pair->one;\n \tstruct diff_filespec *a = ci->ren1->pair->two;\n@@ -1743,7 +1710,7 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, o->path))\n+\t\tif (update_file(opt, 0, &mfi.blob, o->path))\n \t\t\treturn -1;\n \n \t\t/*\n@@ -1754,16 +1721,16 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * such cases, we should keep the added file around,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n-\t\tif (add) {\n-\t\t\tif (update_file(opt, 0, &add->oid, add->mode, a->path))\n+\t\tadd = &ci->ren1->dst_entry->stages[2 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tif (update_file(opt, 0, add, a->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n \t\t\tremove_file_from_index(opt->repo->index, a->path);\n-\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n-\t\tif (add) {\n-\t\t\tif (update_file(opt, 0, &add->oid, add->mode, b->path))\n+\t\tadd = &ci->ren2->dst_entry->stages[3 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tif (update_file(opt, 0, add, b->path))\n \t\t\t\treturn -1;\n \t\t}\n \t\telse\n@@ -1774,40 +1741,42 @@ static int handle_rename_rename_1to2(struct merge_options *opt,\n \t\t * rename/add collision.  If not, we can write the file out\n \t\t * to the specified location.\n \t\t */\n-\t\tadd = filespec_from_entry(&other, ci->ren1->dst_entry, 2 ^ 1);\n-\t\tif (add) {\n+\t\tadd = &ci->ren1->dst_entry->stages[2 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tadd->path = mfi.blob.path = a->path;\n \t\t\tif (handle_file_collision(opt, a->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->ren1->branch,\n \t\t\t\t\t\t  ci->ren2->branch,\n-\t\t\t\t\t\t  &mfi.oid, mfi.mode,\n-\t\t\t\t\t\t  &add->oid, add->mode) < 0)\n+\t\t\t\t\t\t  &mfi.blob, add) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, a->path,\n \t\t\t\t\t\t\t\tci->ren1->branch,\n \t\t\t\t\t\t\t\tci->ren2->branch);\n-\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))\n+\t\t\tif (update_file(opt, 0, &mfi.blob,\n+\t\t\t\t\tnew_path ? new_path : a->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n \t\t\tif (update_stages(opt, a->path, NULL, a, NULL))\n \t\t\t\treturn -1;\n \t\t}\n \n-\t\tadd = filespec_from_entry(&other, ci->ren2->dst_entry, 3 ^ 1);\n-\t\tif (add) {\n+\t\tadd = &ci->ren2->dst_entry->stages[3 ^ 1];\n+\t\tif (is_valid(add)) {\n+\t\t\tadd->path = mfi.blob.path = b->path;\n \t\t\tif (handle_file_collision(opt, b->path,\n \t\t\t\t\t\t  NULL, NULL,\n \t\t\t\t\t\t  ci->ren1->branch,\n \t\t\t\t\t\t  ci->ren2->branch,\n-\t\t\t\t\t\t  &add->oid, add->mode,\n-\t\t\t\t\t\t  &mfi.oid, mfi.mode) < 0)\n+\t\t\t\t\t\t  add, &mfi.blob) < 0)\n \t\t\t\treturn -1;\n \t\t} else {\n \t\t\tchar *new_path = find_path_for_conflict(opt, b->path,\n \t\t\t\t\t\t\t\tci->ren2->branch,\n \t\t\t\t\t\t\t\tci->ren1->branch);\n-\t\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))\n+\t\t\tif (update_file(opt, 0, &mfi.blob,\n+\t\t\t\t\tnew_path ? new_path : b->path))\n \t\t\t\treturn -1;\n \t\t\tfree(new_path);\n \t\t\tif (update_stages(opt, b->path, NULL, NULL, b))\n@@ -1826,12 +1795,12 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \tstruct diff_filespec *b = ci->ren2->pair->one;\n \tstruct diff_filespec *c1 = ci->ren1->pair->two;\n \tstruct diff_filespec *c2 = ci->ren2->pair->two;\n-\tstruct diff_filespec tmp1, tmp2;\n \tchar *path = c1->path; /* == c2->path */\n \tchar *path_side_1_desc;\n \tchar *path_side_2_desc;\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ostage1, ostage2;\n \n \toutput(opt, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1839,27 +1808,31 @@ static int handle_rename_rename_2to1(struct merge_options *opt,\n \t       a->path, c1->path, ci->ren1->branch,\n \t       b->path, c2->path, ci->ren2->branch);\n \n-\tfilespec_from_entry(&tmp1, ci->ren1->src_entry, 3);\n-\ttmp1.path = a->path;\n-\tfilespec_from_entry(&tmp2, ci->ren2->src_entry, 2);\n-\ttmp2.path = b->path;\n-\n \tpath_side_1_desc = xstrfmt(\"version of %s from %s\", path, a->path);\n \tpath_side_2_desc = xstrfmt(\"version of %s from %s\", path, b->path);\n-\tif (merge_mode_and_contents(opt, a, c1, &tmp1, path_side_1_desc,\n+\tostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2;\n+\tostage2 = ostage1 ^ 1;\n+\tci->ren1->src_entry->stages[ostage1].path = a->path;\n+\tci->ren2->src_entry->stages[ostage2].path = b->path;\n+\tif (merge_mode_and_contents(opt, a, c1,\n+\t\t\t\t    &ci->ren1->src_entry->stages[ostage1],\n+\t\t\t\t    path_side_1_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c1) ||\n-\t    merge_mode_and_contents(opt, b, &tmp2, c2, path_side_2_desc,\n+\t    merge_mode_and_contents(opt, b,\n+\t\t\t\t    &ci->ren2->src_entry->stages[ostage2],\n+\t\t\t\t    c2, path_side_2_desc,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    1 + opt->call_depth * 2, &mfi_c2))\n \t\treturn -1;\n \tfree(path_side_1_desc);\n \tfree(path_side_2_desc);\n+\tmfi_c1.blob.path = path;\n+\tmfi_c2.blob.path = path;\n \n \treturn handle_file_collision(opt, path, a->path, b->path,\n \t\t\t\t     ci->ren1->branch, ci->ren2->branch,\n-\t\t\t\t     &mfi_c1.oid, mfi_c1.mode,\n-\t\t\t\t     &mfi_c2.oid, mfi_c2.mode);\n+\t\t\t\t     &mfi_c1.blob, &mfi_c2.blob);\n }\n \n /*\n@@ -2722,7 +2695,6 @@ static int process_renames(struct merge_options *opt,\n \n \t\t\tsetup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,\n \t\t\t\t\t\t   opt, ren1, ren2);\n-\n \t\t} else {\n \t\t\t/* Renamed in 1, maybe changed in 2 */\n \t\t\t/* we only use sha1 and mode of these */\n@@ -2771,8 +2743,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t * update_file().\n \t\t\t\t */\n \t\t\t\tif (update_file_flags(opt,\n-\t\t\t\t\t\t      &ren1->pair->two->oid,\n-\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t      ren1->pair->two,\n \t\t\t\t\t\t      ren1_dst,\n \t\t\t\t\t\t      1, /* update_cache */\n \t\t\t\t\t\t      0  /* update_wd    */))\n@@ -2923,11 +2894,6 @@ static void final_cleanup_renames(struct rename_info *re_info)\n \tfinal_cleanup_rename(re_info->merge_renames);\n }\n \n-static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n-{\n-\treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n-}\n-\n static int read_oid_strbuf(struct merge_options *opt,\n \t\t\t   const struct object_id *oid,\n \t\t\t   struct strbuf *dst)\n@@ -2947,10 +2913,8 @@ static int read_oid_strbuf(struct merge_options *opt,\n }\n \n static int blob_unchanged(struct merge_options *opt,\n-\t\t\t  const struct object_id *o_oid,\n-\t\t\t  unsigned o_mode,\n-\t\t\t  const struct object_id *a_oid,\n-\t\t\t  unsigned a_mode,\n+\t\t\t  const struct diff_filespec *o,\n+\t\t\t  const struct diff_filespec *a,\n \t\t\t  int renormalize, const char *path)\n {\n \tstruct strbuf obuf = STRBUF_INIT;\n@@ -2958,16 +2922,15 @@ static int blob_unchanged(struct merge_options *opt,\n \tint ret = 0; /* assume changed for safety */\n \tconst struct index_state *idx = opt->repo->index;\n \n-\tif (a_mode != o_mode)\n+\tif (a->mode != o->mode)\n \t\treturn 0;\n-\tif (oid_eq(o_oid, a_oid))\n+\tif (oid_eq(&o->oid, &a->oid))\n \t\treturn 1;\n \tif (!renormalize)\n \t\treturn 0;\n \n-\tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(opt, o_oid, &obuf) ||\n-\t    read_oid_strbuf(opt, a_oid, &abuf))\n+\tif (read_oid_strbuf(opt, &o->oid, &obuf) ||\n+\t    read_oid_strbuf(opt, &a->oid, &abuf))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -2986,30 +2949,26 @@ static int blob_unchanged(struct merge_options *opt,\n \n static int handle_modify_delete(struct merge_options *opt,\n \t\t\t\tconst char *path,\n-\t\t\t\tstruct object_id *o_oid, int o_mode,\n-\t\t\t\tstruct object_id *a_oid, int a_mode,\n-\t\t\t\tstruct object_id *b_oid, int b_mode)\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b)\n {\n \tconst char *modify_branch, *delete_branch;\n-\tstruct object_id *changed_oid;\n-\tint changed_mode;\n+\tconst struct diff_filespec *changed;\n \n-\tif (a_oid) {\n+\tif (is_valid(a)) {\n \t\tmodify_branch = opt->branch1;\n \t\tdelete_branch = opt->branch2;\n-\t\tchanged_oid = a_oid;\n-\t\tchanged_mode = a_mode;\n+\t\tchanged = a;\n \t} else {\n \t\tmodify_branch = opt->branch2;\n \t\tdelete_branch = opt->branch1;\n-\t\tchanged_oid = b_oid;\n-\t\tchanged_mode = b_mode;\n+\t\tchanged = b;\n \t}\n \n \treturn handle_change_delete(opt,\n \t\t\t\t    path, NULL,\n-\t\t\t\t    o_oid, o_mode,\n-\t\t\t\t    changed_oid, changed_mode,\n+\t\t\t\t    o, changed,\n \t\t\t\t    modify_branch, delete_branch,\n \t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n@@ -3017,50 +2976,24 @@ static int handle_modify_delete(struct merge_options *opt,\n static int handle_content_merge(struct merge_options *opt,\n \t\t\t\tconst char *path,\n \t\t\t\tint is_dirty,\n-\t\t\t\tstruct object_id *o_oid, int o_mode,\n-\t\t\t\tstruct object_id *a_oid, int a_mode,\n-\t\t\t\tstruct object_id *b_oid, int b_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \tconst char *reason = _(\"content\");\n-\tconst char *path1 = NULL, *path2 = NULL;\n \tstruct merge_file_info mfi;\n-\tstruct diff_filespec one, a, b;\n \tunsigned df_conflict_remains = 0;\n \n-\tif (!o_oid) {\n+\tif (!is_valid(o))\n \t\treason = _(\"add/add\");\n-\t\to_oid = (struct object_id *)&null_oid;\n-\t}\n-\tone.path = a.path = b.path = (char *)path;\n-\toidcpy(&one.oid, o_oid);\n-\tone.mode = o_mode;\n-\toidcpy(&a.oid, a_oid);\n-\ta.mode = a_mode;\n-\toidcpy(&b.oid, b_oid);\n-\tb.mode = b_mode;\n-\n-\tif (ci) {\n-\t\tstruct diff_filepair *pair1 = ci->ren1->pair;\n-\n-\t\tpath1 = (opt->branch1 == ci->ren1->branch) ?\n-\t\t\tpair1->two->path : pair1->one->path;\n-\t\t/* If ci->ren2->pair != NULL, we are in\n-\t\t * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a\n-\t\t * normal rename.\n-\t\t */\n-\t\tpath2 = ((ci->ren2 && ci->ren2->pair) ||\n-\t\t\t opt->branch2 == ci->ren1->branch) ?\n-\t\t\tpair1->two->path : pair1->one->path;\n-\t\tone.path = pair1->one->path;\n-\t\ta.path = (char *)path1;\n-\t\tb.path = (char *)path2;\n-\n-\t\tif (dir_in_way(opt->repo->index, path, !opt->call_depth,\n-\t\t\t       S_ISGITLINK(pair1->two->mode)))\n-\t\t\tdf_conflict_remains = 1;\n-\t}\n-\tif (merge_mode_and_contents(opt, &one, &a, &b, path,\n+\n+\tassert(o->path && a->path && b->path);\n+\tif (ci && dir_in_way(opt->repo->index, path, !opt->call_depth,\n+\t\t\t     S_ISGITLINK(ci->ren1->pair->two->mode)))\n+\t\tdf_conflict_remains = 1;\n+\n+\tif (merge_mode_and_contents(opt, o, a, b, path,\n \t\t\t\t    opt->branch1, opt->branch2,\n \t\t\t\t    opt->call_depth * 2, &mfi))\n \t\treturn -1;\n@@ -3071,14 +3004,13 @@ static int handle_content_merge(struct merge_options *opt,\n \t *   b) The merge matches what was in HEAD (content, mode, pathname)\n \t *   c) The target path is usable (i.e. not involved in D/F conflict)\n \t */\n-\tif (mfi.clean &&\n-\t    was_tracked_and_matches(opt, path, &mfi.oid, mfi.mode) &&\n+\tif (mfi.clean && was_tracked_and_matches(opt, path, &mfi.blob) &&\n \t    !df_conflict_remains) {\n \t\tint pos;\n \t\tstruct cache_entry *ce;\n \n \t\toutput(opt, 3, _(\"Skipped %s (merged same as existing)\"), path);\n-\t\tif (add_cacheinfo(opt, mfi.mode, &mfi.oid, path,\n+\t\tif (add_cacheinfo(opt, &mfi.blob, path,\n \t\t\t\t  0, (!opt->call_depth && !is_dirty), 0))\n \t\t\treturn -1;\n \t\t/*\n@@ -3098,12 +3030,12 @@ static int handle_content_merge(struct merge_options *opt,\n \t}\n \n \tif (!mfi.clean) {\n-\t\tif (S_ISGITLINK(mfi.mode))\n+\t\tif (S_ISGITLINK(mfi.blob.mode))\n \t\t\treason = _(\"submodule\");\n \t\toutput(opt, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (ci && !df_conflict_remains)\n-\t\t\tif (update_stages(opt, path, &one, &a, &b))\n+\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -3113,17 +3045,14 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\tremove_file_from_index(opt->repo->index, path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(opt, path, &one, &a, &b))\n+\t\t\t\tif (update_stages(opt, path, o, a, b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(opt, path);\n-\t\t\t\tstruct diff_filespec merged;\n-\t\t\t\toidcpy(&merged.oid, &mfi.oid);\n-\t\t\t\tmerged.mode = mfi.mode;\n \n \t\t\t\tif (update_stages(opt, path, NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\t\t  file_from_stage2 ? &mfi.blob : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &mfi.blob))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \n@@ -3134,28 +3063,27 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t\t       path);\n \t\t}\n \t\toutput(opt, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tif (update_file(opt, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\tif (update_file(opt, 0, &mfi.blob, new_path)) {\n \t\t\tfree(new_path);\n \t\t\treturn -1;\n \t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else if (update_file(opt, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t} else if (update_file(opt, mfi.clean, &mfi.blob, path))\n \t\treturn -1;\n \treturn !is_dirty && mfi.clean;\n }\n \n static int handle_rename_normal(struct merge_options *opt,\n \t\t\t\tconst char *path,\n-\t\t\t\tstruct object_id *o_oid, unsigned int o_mode,\n-\t\t\t\tstruct object_id *a_oid, unsigned int a_mode,\n-\t\t\t\tstruct object_id *b_oid, unsigned int b_mode,\n+\t\t\t\tconst struct diff_filespec *o,\n+\t\t\t\tconst struct diff_filespec *a,\n+\t\t\t\tconst struct diff_filespec *b,\n \t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Merge the content and write it out */\n \treturn handle_content_merge(opt, path, was_dirty(opt, path),\n-\t\t\t\t    o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,\n-\t\t\t\t    ci);\n+\t\t\t\t    o, a, b, ci);\n }\n \n /* Per entry merge function */\n@@ -3164,24 +3092,36 @@ static int process_entry(struct merge_options *opt,\n {\n \tint clean_merge = 1;\n \tint normalize = opt->renormalize;\n-\tunsigned o_mode = entry->stages[1].mode;\n-\tunsigned a_mode = entry->stages[2].mode;\n-\tunsigned b_mode = entry->stages[3].mode;\n-\tstruct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode);\n-\tstruct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode);\n-\tstruct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode);\n+\n+\tstruct diff_filespec *o = &entry->stages[1];\n+\tstruct diff_filespec *a = &entry->stages[2];\n+\tstruct diff_filespec *b = &entry->stages[3];\n+\tint o_valid = is_valid(o);\n+\tint a_valid = is_valid(a);\n+\tint b_valid = is_valid(b);\n+\to->path = a->path = b->path = (char*)path;\n \n \tentry->processed = 1;\n \tif (entry->rename_conflict_info) {\n \t\tstruct rename_conflict_info *ci = entry->rename_conflict_info;\n+\t\tstruct diff_filespec *temp;\n+\n+\t\t/*\n+\t\t * For cases with a single rename, {o,a,b}->path have all been\n+\t\t * set to the rename target path; we need to set two of these\n+\t\t * back to the rename source.\n+\t\t * For rename/rename conflicts, we'll manually fix paths below.\n+\t\t */\n+\t\ttemp = (opt->branch1 == ci->ren1->branch) ? b : a;\n+\t\to->path = temp->path = ci->ren1->pair->one->path;\n+\t\tif (ci->ren2) {\n+\t\t\tassert(opt->branch1 == ci->ren1->branch);\n+\t\t}\n+\n \t\tswitch (ci->rename_type) {\n \t\tcase RENAME_NORMAL:\n \t\tcase RENAME_ONE_FILE_TO_ONE:\n-\t\t\tclean_merge = handle_rename_normal(opt,\n-\t\t\t\t\t\t\t   path,\n-\t\t\t\t\t\t\t   o_oid, o_mode,\n-\t\t\t\t\t\t\t   a_oid, a_mode,\n-\t\t\t\t\t\t\t   b_oid, b_mode,\n+\t\t\tclean_merge = handle_rename_normal(opt, path, o, a, b,\n \t\t\t\t\t\t\t   ci);\n \t\t\tbreak;\n \t\tcase RENAME_VIA_DIR:\n@@ -3204,11 +3144,27 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n+\t\t\t/*\n+\t\t\t * Manually fix up paths; note:\n+\t\t\t * ren[12]->pair->one->path are equal.\n+\t\t\t */\n+\t\t\to->path = ci->ren1->pair->one->path;\n+\t\t\ta->path = ci->ren1->pair->two->path;\n+\t\t\tb->path = ci->ren2->pair->two->path;\n+\n \t\t\tclean_merge = 0;\n \t\t\tif (handle_rename_rename_1to2(opt, ci))\n \t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n+\t\t\t/*\n+\t\t\t * Manually fix up paths; note,\n+\t\t\t * ren[12]->pair->two->path are actually equal.\n+\t\t\t */\n+\t\t\to->path = NULL;\n+\t\t\ta->path = ci->ren1->pair->two->path;\n+\t\t\tb->path = ci->ren2->pair->two->path;\n+\n \t\t\t/*\n \t\t\t * Probably unclean merge, but if the two renamed\n \t\t\t * files merge cleanly and the two resulting files\n@@ -3221,57 +3177,53 @@ static int process_entry(struct merge_options *opt,\n \t\t\tentry->processed = 0;\n \t\t\tbreak;\n \t\t}\n-\t} else if (o_oid && (!a_oid || !b_oid)) {\n+\t} else if (o_valid && (!a_valid || !b_valid)) {\n \t\t/* Case A: Deleted in one */\n-\t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(opt, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(opt, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\tif ((!a_valid && !b_valid) ||\n+\t\t    (!b_valid && blob_unchanged(opt, o, a, normalize, path)) ||\n+\t\t    (!a_valid && blob_unchanged(opt, o, b, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n-\t\t\tif (a_oid)\n+\t\t\tif (a_valid)\n \t\t\t\toutput(opt, 2, _(\"Removing %s\"), path);\n \t\t\t/* do not touch working file if it did not exist */\n-\t\t\tremove_file(opt, 1, path, !a_oid);\n+\t\t\tremove_file(opt, 1, path, !a_valid);\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\tif (handle_modify_delete(opt, path, o_oid, o_mode,\n-\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n+\t\t\tif (handle_modify_delete(opt, path, o, a, b))\n \t\t\t\tclean_merge = -1;\n \t\t}\n-\t} else if ((!o_oid && a_oid && !b_oid) ||\n-\t\t   (!o_oid && !a_oid && b_oid)) {\n+\t} else if ((!o_valid && a_valid && !b_valid) ||\n+\t\t   (!o_valid && !a_valid && b_valid)) {\n \t\t/* Case B: Added in one. */\n \t\t/* [nothing|directory] -> ([nothing|directory], file) */\n \n \t\tconst char *add_branch;\n \t\tconst char *other_branch;\n-\t\tunsigned mode;\n-\t\tconst struct object_id *oid;\n \t\tconst char *conf;\n+\t\tconst struct diff_filespec *contents;\n \n-\t\tif (a_oid) {\n+\t\tif (a_valid) {\n \t\t\tadd_branch = opt->branch1;\n \t\t\tother_branch = opt->branch2;\n-\t\t\tmode = a_mode;\n-\t\t\toid = a_oid;\n+\t\t\tcontents = a;\n \t\t\tconf = _(\"file/directory\");\n \t\t} else {\n \t\t\tadd_branch = opt->branch2;\n \t\t\tother_branch = opt->branch1;\n-\t\t\tmode = b_mode;\n-\t\t\toid = b_oid;\n+\t\t\tcontents = b;\n \t\t\tconf = _(\"directory/file\");\n \t\t}\n \t\tif (dir_in_way(opt->repo->index, path,\n-\t\t\t       !opt->call_depth && !S_ISGITLINK(a_mode),\n+\t\t\t       !opt->call_depth && !S_ISGITLINK(a->mode),\n \t\t\t       0)) {\n \t\t\tchar *new_path = unique_path(opt, path, add_branch);\n \t\t\tclean_merge = 0;\n \t\t\toutput(opt, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tif (update_file(opt, 0, oid, mode, new_path))\n+\t\t\tif (update_file(opt, 0, contents, new_path))\n \t\t\t\tclean_merge = -1;\n \t\t\telse if (opt->call_depth)\n \t\t\t\tremove_file_from_index(opt->repo->index, path);\n@@ -3279,11 +3231,11 @@ static int process_entry(struct merge_options *opt,\n \t\t} else {\n \t\t\toutput(opt, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tif (update_file_flags(opt, oid, mode, path, 1, !a_oid))\n+\t\t\tif (update_file_flags(opt, contents, path, 1, !a_valid))\n \t\t\t\tclean_merge = -1;\n \t\t}\n-\t} else if (a_oid && b_oid) {\n-\t\tif (!o_oid) {\n+\t} else if (a_valid && b_valid) {\n+\t\tif (!o_valid) {\n \t\t\t/* Case C: Added in both (check for same permissions) */\n \t\t\toutput(opt, 1,\n \t\t\t       _(\"CONFLICT (add/add): Merge conflict in %s\"),\n@@ -3292,24 +3244,19 @@ static int process_entry(struct merge_options *opt,\n \t\t\t\t\t\t\t    path, NULL, NULL,\n \t\t\t\t\t\t\t    opt->branch1,\n \t\t\t\t\t\t\t    opt->branch2,\n-\t\t\t\t\t\t\t    a_oid, a_mode,\n-\t\t\t\t\t\t\t    b_oid, b_mode);\n+\t\t\t\t\t\t\t    a, b);\n \t\t} else {\n \t\t\t/* case D: Modified in both, but differently. */\n \t\t\tint is_dirty = 0; /* unpack_trees would have bailed if dirty */\n-\t\t\tclean_merge = handle_content_merge(opt, path,\n-\t\t\t\t\t\t\t   is_dirty,\n-\t\t\t\t\t\t\t   o_oid, o_mode,\n-\t\t\t\t\t\t\t   a_oid, a_mode,\n-\t\t\t\t\t\t\t   b_oid, b_mode,\n-\t\t\t\t\t\t\t   NULL);\n+\t\t\tclean_merge = handle_content_merge(opt, path, is_dirty,\n+\t\t\t\t\t\t\t   o, a, b, NULL);\n \t\t}\n-\t} else if (!o_oid && !a_oid && !b_oid) {\n+\t} else if (!o_valid && !a_valid && !b_valid) {\n \t\t/*\n \t\t * this entry was deleted altogether. a_mode == 0 means\n \t\t * we had that path and want to actively remove it.\n \t\t */\n-\t\tremove_file(opt, 1, path, !a_mode);\n+\t\tremove_file(opt, 1, path, !a->mode);\n \t} else\n \t\tBUG(\"fatal merge failure, shouldn't happen.\");\n \n-- \n2.21.0.211.g719c25afaf.dirty\n\n"},{"id":"373220","messageId":"CA+P7+xrbd3c7J5tW+LOXKq7g62HL_0GdDOBZhT-oNgRPHOeS+A@mail.gmail.com","threadId":"50603","inReplyTo":"20190405150026.5260-1-newren@gmail.com","subject":"Re: [PATCH v3 00/15] Switch directory rename detection default","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2019-04-05T16:32:06Z","receivedAt":"2019-04-05T16:32:21Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Apr 5, 2019 at 8:03 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> This series adds a new configuration option, merge.directoryRenames,\n> for setting how to make use of directory rename detection heuristics.\n> The default becomes \"conflict\", meaning that conflicts are reported\n> instead of silently moving paths according to the heuristics.  Also,\n> even when merge.directoryRenames config setting is \"true\", this series\n> changes behavior in that it now prints informational messages about\n> paths that are adjusted by the directory rename detection heuristics.\n>\n\nI read through the v2 series, and the range diff on v3. I thought it\nlooked good.\n\n> Changes since v2 (range-diff below):\n>   * Made use of git_parse_maybe_bool() as suggested by Ævar, and made\n>     the parsing of the merge.directoryRenames setting look more like\n>     that for merge.ff.\n>\n> I didn't get much review of round 2, which maybe means everyone is\n> happy with what they see.  If anyone would like to take a look at just\n> part of the series, the pieces I'd most like folks to look at are:\n>   * Patch 15, particularly looking over the new testcases (13a-13d) in\n>     t6043 and the documentation.\n\nThe documentation made sense to me. I can't speak much towards the\nimplementation, but I definitely agree that conflicting is a suitable\ndefault, and better than silently renaming.\n\n>   * Should I have switched the type of \"mode\" from 'unsigned short' to\n>     'unsigned' instead of vice-versa in patch 1?\n\nI think switching to unsigned short is better. Unless we need the full\ninteger width for some reason? but since it's already short I don't\nsee why we would..\n\n>   * Similarly, does anyone have a reason to prefer oid,mode pair over\n>     using a diff_filespec (in patch 11 I convert half the sites to the\n>     latter)?\n>\n\n\n>\n> Elijah Newren (15):\n>   Use 'unsigned short' for mode, like diff_filespec does\n>   merge-recursive: rename merge_options argument from 'o' to 'opt'\n>   merge-recursive: rename diff_filespec 'one' to 'o'\n>   merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'\n>   merge-recursive: use 'ci' for rename_conflict_info variable name\n>   merge-recursive: move some struct declarations together\n>   merge-recursive: shrink rename_conflict_info\n>   merge-recursive: remove ren[12]_other fields from rename_conflict_info\n>   merge-recursive: track branch where rename occurred in rename struct\n>   merge-recursive: cleanup handle_rename_* function signatures\n>   merge-recursive: switch from (oid,mode) pairs to a diff_filespec\n>   t6043: fix copied test description to match its purpose\n>   merge-recursive: track information associated with directory renames\n>   merge-recursive: give callers of handle_content_merge() access to\n>     contents\n>   merge-recursive: switch directory rename detection default\n>\n>  Documentation/config/merge.txt         |   19 +-\n>  archive.c                              |    2 +-\n>  blame.c                                |    2 +-\n>  blame.h                                |    2 +-\n>  builtin/rm.c                           |    2 +-\n>  builtin/update-index.c                 |    2 +-\n>  cache.h                                |    2 +-\n>  fsck.c                                 |    2 +-\n>  line-log.c                             |    2 +-\n>  match-trees.c                          |    8 +-\n>  merge-recursive.c                      | 1853 ++++++++++++------------\n>  notes.c                                |    2 +-\n>  sha1-name.c                            |    2 +-\n>  t/t3401-rebase-and-am-rename.sh        |    8 +-\n>  t/t6043-merge-rename-directories.sh    |  462 +++++-\n>  t/t6046-merge-skip-unneeded-updates.sh |    8 +-\n>  tree-diff.c                            |    2 +-\n>  tree-walk.c                            |    6 +-\n>  tree-walk.h                            |    6 +-\n>  19 files changed, 1367 insertions(+), 1025 deletions(-)\n>\n> Range-diff:\n>  1:  bb5b410a61 =  1:  bb5b410a61 Use 'unsigned short' for mode, like diff_filespec does\n>  2:  f91c28257e =  2:  f91c28257e merge-recursive: rename merge_options argument from 'o' to 'opt'\n>  3:  e3fe8baa15 =  3:  e3fe8baa15 merge-recursive: rename diff_filespec 'one' to 'o'\n>  4:  c6bd963ffb =  4:  c6bd963ffb merge-recursive: rename locals 'o' and 'a' to 'obuf' and 'abuf'\n>  5:  eca30e7571 =  5:  eca30e7571 merge-recursive: use 'ci' for rename_conflict_info variable name\n>  6:  07f0d5fa8e =  6:  07f0d5fa8e merge-recursive: move some struct declarations together\n>  7:  4cdd1ecbcb =  7:  4cdd1ecbcb merge-recursive: shrink rename_conflict_info\n>  8:  3490324bdd =  8:  3490324bdd merge-recursive: remove ren[12]_other fields from rename_conflict_info\n>  9:  fb73a2c55d =  9:  fb73a2c55d merge-recursive: track branch where rename occurred in rename struct\n> 10:  124ee08ed8 = 10:  124ee08ed8 merge-recursive: cleanup handle_rename_* function signatures\n> 11:  78a5916efe = 11:  78a5916efe merge-recursive: switch from (oid,mode) pairs to a diff_filespec\n> 12:  a8309326c1 = 12:  a8309326c1 t6043: fix copied test description to match its purpose\n> 13:  b362f4db1e = 13:  b362f4db1e merge-recursive: track information associated with directory renames\n> 14:  2e0258a358 = 14:  2e0258a358 merge-recursive: give callers of handle_content_merge() access to contents\n> 15:  719c25afaf ! 15:  428cdf62b3 merge-recursive: switch directory rename detection default\n>     @@ -262,17 +262,12 @@\n>                 free(value);\n>         }\n>      +  if (!git_config_get_string(\"merge.directoryrenames\", &value)) {\n>     -+          if (!strcasecmp(value, \"true\"))\n>     -+                  opt->detect_directory_renames = 2;\n>     -+          else if (!strcasecmp(value, \"false\"))\n>     -+                  opt->detect_directory_renames = 0;\n>     -+          else if (!strcasecmp(value, \"conflict\"))\n>     ++          int boolval = git_parse_maybe_bool(value);\n>     ++          if (0 <= boolval) {\n>     ++                  opt->detect_directory_renames = boolval ? 2 : 0;\n>     ++          } else if (!strcasecmp(value, \"conflict\")) {\n>      +                  opt->detect_directory_renames = 1;\n>     -+          else {\n>     -+                  error(_(\"Invalid value for merge.directoryRenames: %s\"),\n>     -+                        value);\n>     -+                  opt->detect_directory_renames = 1;\n>     -+          }\n>     ++          } /* avoid erroring on values from future versions of git */\n>      +          free(value);\n>      +  }\n>         git_config(git_xmerge_config, NULL);\n> --\n> 2.21.0.211.g719c25afaf.dirty\n>\n"}]}