{"thread":{"id":"32046","subject":"Rename edge case...","startedAt":"2012-11-09T09:10:31Z","lastAt":"2012-11-10T02:01:03Z","messageCount":7,"participants":["John Szakmeister","Tomas Carnecky","Nguyen Thai Ngoc Duy","Johannes Sixt","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"202686","messageId":"CAEBDL5U+OSTCAqgWoApE_m21Nef24Wqvt78oB6qqV4oEvU0vXQ@mail.gmail.com","threadId":"32046","inReplyTo":null,"subject":"Rename edge case...","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2012-11-09T09:10:31Z","receivedAt":"2012-11-09T09:10:31Z","isPatch":false,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"I've been browsing StackOverflow answering git-related questions, and\nran across this one:\n    <http://stackoverflow.com/questions/13300675/git-merge-rename-conflict>\n\nIt's a bit of an interesting situation.  The user did a couple of\nrenames in a branch:\n    foo.txt => fooOld.txt\n    fooNew.txt => foo.txt\n\nMeanwhile, master had an update to fooNew.txt.  When the user tried to\nmerge master to the branch, it gave a merge conflict saying fooNew.txt\nwas deleted, but master tried to update it.\n\nI was a bit surprised that git didn't follow the rename here, though I\ndo understand why: git only sees it as a rename if the source\ndisappears completely.  So I played locally with a few ideas, and was\nsurprised to find out that even breaking up the two renames into two\nseparate commits git still didn't follow it.\n\nI'm just curious--I don't run into this often myself--but is there a\ngood strategy for dealing with this that avoids the conflict?\n\nThanks!\n\n-John\n"},{"id":"202688","messageId":"1352453243-ner-1164@calvin","threadId":"32046","inReplyTo":"CAEBDL5U+OSTCAqgWoApE_m21Nef24Wqvt78oB6qqV4oEvU0vXQ@mail.gmail.com","subject":"Re: Rename edge case...","fromName":"Tomas Carnecky","fromEmail":"tomas.carnecky@gmail.com","sentAt":"2012-11-09T09:27:23Z","receivedAt":"2012-11-09T09:27:23Z","isPatch":false,"sender":{"key":"tomas.carnecky@gmail.com","avatar":null},"body":"On Fri, 09 Nov 2012 04:10:31 -0500, John Szakmeister <john@szakmeister.net> wrote:\n> I've been browsing StackOverflow answering git-related questions, and\n> ran across this one:\n>     <http://stackoverflow.com/questions/13300675/git-merge-rename-conflict>\n> \n> It's a bit of an interesting situation.  The user did a couple of\n> renames in a branch:\n>     foo.txt => fooOld.txt\n>     fooNew.txt => foo.txt\n> \n> Meanwhile, master had an update to fooNew.txt.  When the user tried to\n> merge master to the branch, it gave a merge conflict saying fooNew.txt\n> was deleted, but master tried to update it.\n> \n> I was a bit surprised that git didn't follow the rename here, though I\n> do understand why: git only sees it as a rename if the source\n> disappears completely.  So I played locally with a few ideas, and was\n> surprised to find out that even breaking up the two renames into two\n> separate commits git still didn't follow it.\n> \n> I'm just curious--I don't run into this often myself--but is there a\n> good strategy for dealing with this that avoids the conflict?\n\nWhen merging two branches, git only looks at the tips. It doesn't inspect\ntheir histories to see how the files were moved around. So i doesn't matter\nwhether you rename the files in a single commit or multiple commits. The\nresulting tree is always the same.\n\ntom\n"},{"id":"202689","messageId":"CAEBDL5WeQEWdyaJuuNbnnQbbsLYv8NO1ZSj3eHHpjW+ToS9X1A@mail.gmail.com","threadId":"32046","inReplyTo":"1352453243-ner-1164@calvin","subject":"Re: Rename edge case...","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2012-11-09T10:25:26Z","receivedAt":"2012-11-09T10:25:26Z","isPatch":false,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"On Fri, Nov 9, 2012 at 4:27 AM, Tomas Carnecky <tomas.carnecky@gmail.com> wrote:\n[snip]\n> When merging two branches, git only looks at the tips. It doesn't inspect\n> their histories to see how the files were moved around. So i doesn't matter\n> whether you rename the files in a single commit or multiple commits. The\n> resulting tree is always the same.\n\nI guess I figured that when I saw the final result, but didn't know if\nthere was a way to coax Git into doing a better job here.  I guess not\nthough.  At least it's a situation that doesn't come up often.\n\n-John\n"},{"id":"202690","messageId":"CACsJy8C6RSnJL=1kWZY8JjXOE9EJGVQL-FFhMN-DNnv9EhH3Cw@mail.gmail.com","threadId":"32046","inReplyTo":"CAEBDL5WeQEWdyaJuuNbnnQbbsLYv8NO1ZSj3eHHpjW+ToS9X1A@mail.gmail.com","subject":"Re: Rename edge case...","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-09T10:30:24Z","receivedAt":"2012-11-09T10:30:24Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Nov 9, 2012 at 5:25 PM, John Szakmeister <john@szakmeister.net> wrote:\n> On Fri, Nov 9, 2012 at 4:27 AM, Tomas Carnecky <tomas.carnecky@gmail.com> wrote:\n> [snip]\n>> When merging two branches, git only looks at the tips. It doesn't inspect\n>> their histories to see how the files were moved around. So i doesn't matter\n>> whether you rename the files in a single commit or multiple commits. The\n>> resulting tree is always the same.\n>\n> I guess I figured that when I saw the final result, but didn't know if\n> there was a way to coax Git into doing a better job here.  I guess not\n> though.\n\nThere is not (yet). There were a few patches around that allow users\nto override git's automatic renames. You can search the mail archive.\nI think the keywords are \"rename cache\". Thanks for reminding me for\nputting a higher priority on that.\n-- \nDuy\n"},{"id":"202693","messageId":"509D0252.8070901@viscovery.net","threadId":"32046","inReplyTo":"CAEBDL5WeQEWdyaJuuNbnnQbbsLYv8NO1ZSj3eHHpjW+ToS9X1A@mail.gmail.com","subject":"Re: Rename edge case...","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-11-09T13:17:06Z","receivedAt":"2012-11-09T13:17:06Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 11/9/2012 11:25, schrieb John Szakmeister:\n> On Fri, Nov 9, 2012 at 4:27 AM, Tomas Carnecky <tomas.carnecky@gmail.com> wrote:\n> [snip]\n>> When merging two branches, git only looks at the tips. It doesn't inspect\n>> their histories to see how the files were moved around. So i doesn't matter\n>> whether you rename the files in a single commit or multiple commits. The\n>> resulting tree is always the same.\n> \n> I guess I figured that when I saw the final result, but didn't know if\n> there was a way to coax Git into doing a better job here.\n\nIf the renames are split in two commits, you can merge the first, and then\nthe second on top of the result.\n\n-- Hannes\n"},{"id":"202702","messageId":"20121109160925.GA19725@sigill.intra.peff.net","threadId":"32046","inReplyTo":"CAEBDL5U+OSTCAqgWoApE_m21Nef24Wqvt78oB6qqV4oEvU0vXQ@mail.gmail.com","subject":"Re: Rename edge case...","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-11-09T16:09:26Z","receivedAt":"2012-11-09T16:09:26Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Nov 09, 2012 at 04:10:31AM -0500, John Szakmeister wrote:\n\n> I've been browsing StackOverflow answering git-related questions, and\n> ran across this one:\n>     <http://stackoverflow.com/questions/13300675/git-merge-rename-conflict>\n> \n> It's a bit of an interesting situation.  The user did a couple of\n> renames in a branch:\n>     foo.txt => fooOld.txt\n>     fooNew.txt => foo.txt\n> \n> Meanwhile, master had an update to fooNew.txt.  When the user tried to\n> merge master to the branch, it gave a merge conflict saying fooNew.txt\n> was deleted, but master tried to update it.\n> \n> I was a bit surprised that git didn't follow the rename here, though I\n> do understand why: git only sees it as a rename if the source\n> disappears completely.\n\nRight. If the source didn't go away, it would be a copy. We can do copy\ndetection, but it is not quite as obvious what a merge should do with a\ncopy (apply the change to the original? To the copy? In both places? You\nwould really want hunk-level copy detection for it to make any sense).\n\nUsually git deals with this double-rename case through the use of\n\"break\" or \"rewrite\" detection. We notice that the old \"foo.txt\" and the\nnew \"foo.txt\" do not look very much like each other, and break the\nmodification apart into an add and a delete. That makes each side\neligible for rename detection, and we can end up finding the pairs of\nrenames above.\n\nSo in theory it just as simple as a one-liner to turn on break-detection\nin merge-recursive. Sadly, that only reveals more issues with how\nmerge-recursive handles renames. See this thread, which has pointers to\nthe breakages at the end:\n\n  http://thread.gmane.org/gmane.comp.version-control.git/169944\n\nI've become convinced that the best way forward with merge-recursive is\nto scrap and rewrite it. It tries to do things in a muddled order, which\nmakes it very brittle to changes like this. I think it needs to have an\ninternal representation of the tree that can represent all of the\nconflicts, and then follow a few simple phases:\n\n  1. \"structural\" 3-way merge handling renames, breaks, typechanges,\n     etc. Each path in tree might show things like D/F conflicts, or it\n     might show content-level merges that still need to happen, even if\n     the content from those merges is not coming from the same paths in\n     the source trees.\n\n  2. Resolve content-level 3-way merges at each path.\n\n  3. Compare the proposed tree to the working tree and list any problems\n     (e.g., untracked files or local modifications that will be\n     overwritten).\n\nRight now it tries to do these things interleaved as it processes paths,\nand as a result we've had many bugs (e.g., the content-level merge\nconflating the content originally at a path and something that was\nrenamed into place, and missing corner cases where we actually overwrite\nuntracked files that should be considered precious).\n\nBut that is just off the top of my head. I haven't looked at the topic\nin quite a while (and I haven't even started working on any such\nrewrite).\n\n> So I played locally with a few ideas, and was surprised to find out\n> that even breaking up the two renames into two separate commits git\n> still didn't follow it.\n\nRight, because the merge only looks at the end points. Try doing a\n\"diff -M\" between your endpoints with and without \"-B\". We do not have\nany double-renames in git.git, but you can find \"-B\" helping a similar\ncase: most of a file's content is moved elsewhere, but some small amount\nremains. For example, try this in git.git, with and without -B:\n\n  git show -M --stat --summary --patch 043a449\n\nIt finds the rename only with \"-B\", which would help a merge (it also\nmakes the diff shorter and more readable, as you can see what was\nchanged as the content migrated to the new file).\n\n-Peff\n"},{"id":"202734","messageId":"CAEBDL5UGxGqE+-P54KeZnV=2Tx6Rpx=MXowJ9RdH5WPuDTg0hw@mail.gmail.com","threadId":"32046","inReplyTo":"20121109160925.GA19725@sigill.intra.peff.net","subject":"Re: Rename edge case...","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2012-11-10T02:01:03Z","receivedAt":"2012-11-10T02:01:03Z","isPatch":false,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"On Fri, Nov 9, 2012 at 11:09 AM, Jeff King <peff@peff.net> wrote:\n[snip]\n> Right. If the source didn't go away, it would be a copy. We can do copy\n> detection, but it is not quite as obvious what a merge should do with a\n> copy (apply the change to the original? To the copy? In both places? You\n> would really want hunk-level copy detection for it to make any sense).\n\nYeah, I wasn't advocating that.  More along the lines of what you're\ntalking about below...\n\n> Usually git deals with this double-rename case through the use of\n> \"break\" or \"rewrite\" detection. We notice that the old \"foo.txt\" and the\n> new \"foo.txt\" do not look very much like each other, and break the\n> modification apart into an add and a delete. That makes each side\n> eligible for rename detection, and we can end up finding the pairs of\n> renames above.\n\nI did try using the -B option, and it did detect that foo.txt was\nrenamed to fooOld.txt, but it didn't show fooNew.txt being renamed to\nfoo.txt.  I'm running git 1.7.12.3.  It could be that 1.8.0 does\nbetter, but I haven't tried.\n\n> So in theory it just as simple as a one-liner to turn on break-detection\n> in merge-recursive. Sadly, that only reveals more issues with how\n> merge-recursive handles renames. See this thread, which has pointers to\n> the breakages at the end:\n>\n>   http://thread.gmane.org/gmane.comp.version-control.git/169944\n\nThank you.  I'll definitely read up on this.\n\n> I've become convinced that the best way forward with merge-recursive is\n> to scrap and rewrite it. It tries to do things in a muddled order, which\n> makes it very brittle to changes like this. I think it needs to have an\n> internal representation of the tree that can represent all of the\n> conflicts, and then follow a few simple phases:\n>\n>   1. \"structural\" 3-way merge handling renames, breaks, typechanges,\n>      etc. Each path in tree might show things like D/F conflicts, or it\n>      might show content-level merges that still need to happen, even if\n>      the content from those merges is not coming from the same paths in\n>      the source trees.\n>\n>   2. Resolve content-level 3-way merges at each path.\n>\n>   3. Compare the proposed tree to the working tree and list any problems\n>      (e.g., untracked files or local modifications that will be\n>      overwritten).\n>\n> Right now it tries to do these things interleaved as it processes paths,\n> and as a result we've had many bugs (e.g., the content-level merge\n> conflating the content originally at a path and something that was\n> renamed into place, and missing corner cases where we actually overwrite\n> untracked files that should be considered precious).\n>\n> But that is just off the top of my head. I haven't looked at the topic\n> in quite a while (and I haven't even started working on any such\n> rewrite).\n\nThat certainly sounds like a better approach.\n\n>> So I played locally with a few ideas, and was surprised to find out\n>> that even breaking up the two renames into two separate commits git\n>> still didn't follow it.\n>\n> Right, because the merge only looks at the end points. Try doing a\n> \"diff -M\" between your endpoints with and without \"-B\". We do not have\n> any double-renames in git.git, but you can find \"-B\" helping a similar\n> case: most of a file's content is moved elsewhere, but some small amount\n> remains. For example, try this in git.git, with and without -B:\n>\n>   git show -M --stat --summary --patch 043a449\n>\n> It finds the rename only with \"-B\", which would help a merge (it also\n> makes the diff shorter and more readable, as you can see what was\n> changed as the content migrated to the new file).\n\nI've played with the -B option before, and it's definitely nice in\ncertain cases.\n\nThank you for taking the time to write all this up.  It was very informative!\n\n-John\n"}]}