{"thread":{"id":"48009","subject":"Rename of file is causing changes to be lost","startedAt":"2018-03-08T16:01:45Z","lastAt":"2018-03-13T18:02:12Z","messageCount":4,"participants":["Robert Dailey","Elijah Newren"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"341294","messageId":"CAHd499Axo7HFviUJavigTZ6BGZCkj9iOSeNVndu1oPivkPv+5Q@mail.gmail.com","threadId":"48009","inReplyTo":null,"subject":"Rename of file is causing changes to be lost","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2018-03-08T16:01:12Z","receivedAt":"2018-03-08T16:01:45Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"I'm on Windows and core.ignorecase is set to 'true' when I clone/init\na repository. I've got a branch where I started making changes to a\nfile AND renamed it only to change its case. The changes I've made\nwere significant enough that git no longer detects a rename, instead\nthe files show up as \"D\" and \"A\" in git status (deleted then added).\nTo correct this, I do an interactive rebase to add an additional\ncommit before the first one to rename the file without changing it,\nand *then* allow the second commit to change the file. The goal is\nthat rebase should detect the rename and automatically move the\nchanges in the (now) second commit to the newly named file. Here's a\nMCVE (treat this as a script):\n\n#/bin/bash\ngit init testgitrepo\ncd testgitrepo/\ngit config core.ignorecase true # This is set by Windows for me, but\nhopefully will allow this to repro on linux. Didn't test linux though.\necho \"first change\" > foo.txt\ngit add . && git commit -m 'first change'\ngit checkout -b topic\necho \"second change\" > foo.txt\ngit mv foo.txt FOO.txt\ngit add . && git commit -m 'second change'\ngit rebase -i master # Move line 1 to line 2, and put \"x false\" in line 1\ngit mv foo.txt FOO.txt && git commit -m 'rename foo'\ngit rebase --continue\ngit mergetool\n\nAfter the rebase continue, you will get a conflict like so:\n\nerror: could not apply 527d208... second change\n\nWhen you have resolved this problem, run \"git rebase --continue\".\nIf you prefer to skip this patch, run \"git rebase --skip\" instead.\nTo check out the original branch and stop rebasing, run \"git rebase --abort\".\n\nCould not apply 527d208... second change\nCONFLICT (rename/delete): foo.txt deleted in 527d208... second change\nand renamed to FOO.txt in HEAD. Version HEAD of FOO.txt left in tree.\n\nThe last command, `git mergetool` runs, giving you the option to pick\nthe Created (left) or Deleted (right) version of the file:\n\nLeft: The file is created, but selecting this erases the changes from\nthe \"added\" version on the remote (which is topic). Basically the\nrename of only case confused git, and we lost the changes on the\nremote version of the file\nRight: File is deleted. Changes are still lost.\n\nThe ideal outcome is that the changes from the \"added\" version of the\nfile in the 2nd commit get carried over to the \"renamed\" version of\nthe file, which when you compare the two are named exactly the same\nafter the 1st commit is introduced. How can I solve this issue?\n"},{"id":"341540","messageId":"CAHd499CKneW5FoeOCgfv4uzvuOVsV-c5FKH_MgDDEgSsJJ7PTg@mail.gmail.com","threadId":"48009","inReplyTo":"CAHd499Axo7HFviUJavigTZ6BGZCkj9iOSeNVndu1oPivkPv+5Q@mail.gmail.com","subject":"Re: Rename of file is causing changes to be lost","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2018-03-13T14:32:06Z","receivedAt":"2018-03-13T14:32:14Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"On Thu, Mar 8, 2018 at 10:01 AM, Robert Dailey <rcdailey.lists@gmail.com> wrote:\n> I'm on Windows and core.ignorecase is set to 'true' when I clone/init\n> a repository. I've got a branch where I started making changes to a\n> file AND renamed it only to change its case. The changes I've made\n> were significant enough that git no longer detects a rename, instead\n> the files show up as \"D\" and \"A\" in git status (deleted then added).\n> To correct this, I do an interactive rebase to add an additional\n> commit before the first one to rename the file without changing it,\n> and *then* allow the second commit to change the file. The goal is\n> that rebase should detect the rename and automatically move the\n> changes in the (now) second commit to the newly named file. Here's a\n> MCVE (treat this as a script):\n>\n> #/bin/bash\n> git init testgitrepo\n> cd testgitrepo/\n> git config core.ignorecase true # This is set by Windows for me, but\n> hopefully will allow this to repro on linux. Didn't test linux though.\n> echo \"first change\" > foo.txt\n> git add . && git commit -m 'first change'\n> git checkout -b topic\n> echo \"second change\" > foo.txt\n> git mv foo.txt FOO.txt\n> git add . && git commit -m 'second change'\n> git rebase -i master # Move line 1 to line 2, and put \"x false\" in line 1\n> git mv foo.txt FOO.txt && git commit -m 'rename foo'\n> git rebase --continue\n> git mergetool\n>\n> After the rebase continue, you will get a conflict like so:\n>\n> error: could not apply 527d208... second change\n>\n> When you have resolved this problem, run \"git rebase --continue\".\n> If you prefer to skip this patch, run \"git rebase --skip\" instead.\n> To check out the original branch and stop rebasing, run \"git rebase --abort\".\n>\n> Could not apply 527d208... second change\n> CONFLICT (rename/delete): foo.txt deleted in 527d208... second change\n> and renamed to FOO.txt in HEAD. Version HEAD of FOO.txt left in tree.\n>\n> The last command, `git mergetool` runs, giving you the option to pick\n> the Created (left) or Deleted (right) version of the file:\n>\n> Left: The file is created, but selecting this erases the changes from\n> the \"added\" version on the remote (which is topic). Basically the\n> rename of only case confused git, and we lost the changes on the\n> remote version of the file\n> Right: File is deleted. Changes are still lost.\n>\n> The ideal outcome is that the changes from the \"added\" version of the\n> file in the 2nd commit get carried over to the \"renamed\" version of\n> the file, which when you compare the two are named exactly the same\n> after the 1st commit is introduced. How can I solve this issue?\n\nI wanted to bump this and see if anyone would have a spare few minutes\nto help me out with this. I know it's a needlessly complex situation,\nbut I'd really love to know how to resolve the specific issue, or\nperhaps learn a better way to approach this in the future if I'm\nshooting myself in the foot here.\n\nThanks again.\n"},{"id":"341552","messageId":"CABPp-BEC48EoTc5eyLrcaLLtUexgbShQsB7zbQJB0QE6AA19kA@mail.gmail.com","threadId":"48009","inReplyTo":"CAHd499Axo7HFviUJavigTZ6BGZCkj9iOSeNVndu1oPivkPv+5Q@mail.gmail.com","subject":"Re: Rename of file is causing changes to be lost","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-03-13T16:51:05Z","receivedAt":"2018-03-13T16:51:14Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Mar 8, 2018 at 8:01 AM, Robert Dailey <rcdailey.lists@gmail.com> wrote:\n> I'm on Windows and core.ignorecase is set to 'true' when I clone/init\n> a repository. I've got a branch where I started making changes to a\n> file AND renamed it only to change its case. The changes I've made\n> were significant enough that git no longer detects a rename, instead\n> the files show up as \"D\" and \"A\" in git status (deleted then added).\n> To correct this, I do an interactive rebase to add an additional\n> commit before the first one to rename the file without changing it,\n> and *then* allow the second commit to change the file. The goal is\n> that rebase should detect the rename and automatically move the\n> changes in the (now) second commit to the newly named file. Here's a\n> MCVE (treat this as a script):\n>\n> #/bin/bash\n> git init testgitrepo\n> cd testgitrepo/\n> git config core.ignorecase true # This is set by Windows for me, but\n> hopefully will allow this to repro on linux. Didn't test linux though.\n> echo \"first change\" > foo.txt\n> git add . && git commit -m 'first change'\n> git checkout -b topic\n> echo \"second change\" > foo.txt\n> git mv foo.txt FOO.txt\n> git add . && git commit -m 'second change'\n> git rebase -i master # Move line 1 to line 2, and put \"x false\" in line 1\n> git mv foo.txt FOO.txt && git commit -m 'rename foo'\n> git rebase --continue\n> git mergetool\n>\n> After the rebase continue, you will get a conflict like so:\n>\n> error: could not apply 527d208... second change\n>\n> When you have resolved this problem, run \"git rebase --continue\".\n> If you prefer to skip this patch, run \"git rebase --skip\" instead.\n> To check out the original branch and stop rebasing, run \"git rebase --abort\".\n>\n> Could not apply 527d208... second change\n> CONFLICT (rename/delete): foo.txt deleted in 527d208... second change\n> and renamed to FOO.txt in HEAD. Version HEAD of FOO.txt left in tree.\n>\n> The last command, `git mergetool` runs, giving you the option to pick\n> the Created (left) or Deleted (right) version of the file:\n>\n> Left: The file is created, but selecting this erases the changes from\n> the \"added\" version on the remote (which is topic). Basically the\n> rename of only case confused git, and we lost the changes on the\n> remote version of the file\n> Right: File is deleted. Changes are still lost.\n>\n> The ideal outcome is that the changes from the \"added\" version of the\n> file in the 2nd commit get carried over to the \"renamed\" version of\n> the file, which when you compare the two are named exactly the same\n> after the 1st commit is introduced. How can I solve this issue?\n\nCool, thanks for the testcase.  I don't have a good workaround for\nyou, but this is clearly a bug in the merge-recursive logic in git.  I\nguess it's what might be called a rename/add/delete conflict, which\ngit just doesn't handle.  Your testcase triggers the bug just fine on\nlinux, though you can trigger the exact same bug without case\nsensitivity using a slightly different setup (and no need for an\ninteractive rebase):\n\n------\ngit init foobar\ncd foobar\necho \"original file\" >foo\ngit add foo\ngit commit -m \"original\"\ngit branch L\ngit branch R\n\ngit checkout L\ngit rm foo\necho \"different file\" >bar\ngit add bar\ngit commit -m \"Remove foo, add bar\"\n\ngit checkout R\ngit mv foo bar\ngit commit -m \"rename foo to bar\"\n\ngit merge L\n-------\n\ngit has code to handle rename/delete conflicts and rename/add\nconflicts, but since one side of history BOTH deleted foo and added an\nunrelated bar, that means both types of changes are relevant to the\nsame path (bar) -- essentially, a rename/delete/add conflict.  Sadly,\ngit only goes down a codepath that handles one of those two (the\nrename/delete), and incorrectly throws away the separate add:\n\n-----\n$ git ls-files -s\n100644 78fa0f415ae2bdb5c0182c067eacaaf843699b39 2    bar\n\n$ git ls-tree -r master\n100644 blob 78fa0f415ae2bdb5c0182c067eacaaf843699b39    foo\n$ git ls-tree -r L\n100644 blob f286e5cdd97ac6895438ea4548638bb98ac9bd6b    bar\n$ git ls-tree -r R\n100644 blob 78fa0f415ae2bdb5c0182c067eacaaf843699b39    bar\n-----\n\nBut the problem is actually a bit bigger than shown here; there are\nhigher order corner cases here too.  I realized in the past that e.g.\nrename/rename(1to2) could also have rename/add conflicts for each\nrename (thus making a rename/rename/add/add conflict possible), but I\nalso felt there were probably some other bad interactions out there.\nI figured they were likely theoretical only, so I didn't bother\ninvestigating.  But, combining your example with that other one, we\nshould also be able to come up with a\nrename/rename/add/add/delete/delete conflict.  I wonder if there are\nothers...\n\nAnyway, I recorded this at\nhttps://bugs.chromium.org/p/git/issues/detail?id=11.  Sorry I don't\nhave a workaround, but I'll try to eventually get back to this and fix\nit.\n"},{"id":"341560","messageId":"CAHd499AyfH3o7wU8S++40ryRHmf++zkE93uMxkVhBx0hWMcr+A@mail.gmail.com","threadId":"48009","inReplyTo":"CABPp-BEC48EoTc5eyLrcaLLtUexgbShQsB7zbQJB0QE6AA19kA@mail.gmail.com","subject":"Re: Rename of file is causing changes to be lost","fromName":"Robert Dailey","fromEmail":"rcdailey.lists@gmail.com","sentAt":"2018-03-13T18:02:05Z","receivedAt":"2018-03-13T18:02:12Z","isPatch":false,"sender":{"key":"rcdailey.lists@gmail.com","avatar":null},"body":"On Tue, Mar 13, 2018 at 11:51 AM, Elijah Newren <newren@gmail.com> wrote:\n> Anyway, I recorded this at\n> https://bugs.chromium.org/p/git/issues/detail?id=11.  Sorry I don't\n> have a workaround, but I'll try to eventually get back to this and fix\n> it.\n\nThank you for taking the time to verify this for me. I will keep an\neye on the issue you linked!\n"}]}