4

I wrote this script and ran it on OSX 10.8 Mountain Lion, on the default case-insensitive HFS filesystem.

#!/bin/sh -x
# create git repo
rm -rf caps
git init caps
cd caps
# commit empty file called "file"
touch file
git add .
git commit -am "initial commit"
# create branch called "branch"
git branch branch
# rename "file" to "File"
# using --force per http://stackoverflow.com/questions/6899582
git mv --force file File
git commit -am "renamed capital"
# switch to branch, make a non-conflicting commit
git checkout branch
touch newfile
git add .
git commit -am "branch commit"
# merge master into branch, commit merge
git merge --commit --no-edit master
# but where's the renamed File?
more File

When the script was done, it failed on the last line, when it should have succeeded:

+ git merge --commit --no-edit master
Removing file
Merge made by the 'recursive' strategy.
 file => File | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename file => File (100%)
+ more File
File: No such file or directory

git status showed this:

$ git status
# On branch branch
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    File
#
no changes added to commit (use "git add" and/or "git commit -a")

We can get the file back at that point with git checkout File, but it's very easy at that point to accidentally commit the deletion.

We've been renaming quite a few files lately, fixing their capitalization, and this has bit our team a lot. Is there a git setting or best practice we can use to workaround this problem? (For now, we're just telling everybody to be extra extra careful.)

Dan Fabulich
  • 37,506
  • 41
  • 139
  • 175
  • Interesting. What is your `core.ignorecase` setting? – Edward Thomson Oct 09 '13 at 19:44
  • I'm on OSX, so git-init and git-clone automatically set core.ignorecase to true. If I set it to false, I'm unable to merge from master at all, with this error: "error: The following untracked working tree files would be overwritten by merge: File". – Dan Fabulich Oct 09 '13 at 23:10
  • I can't reproduce this with git 1.7.5.4 on Mac OS or Git for Windows 1.8.4. `file` is accurately renamed to `File` for me, following the steps you provided. – Edward Thomson Oct 10 '13 at 01:50
  • I agree that it doesn't repro on git 1.7.5.4 for OS X, but it does repro on 1.8.4. That let me run a bisect which turned up https://github.com/git/git/commit/4f7cb99ada26be5d86402a6e060f3ee16a672f16 which indicates that the problem only occurs when renaming empty files. (I bet you didn't use an empty file on Windows?) – Dan Fabulich Oct 10 '13 at 17:00
  • Interesting. I did not use empty files; I wouldn't have expected that you had a lot of empty files that needed case-changing renames. Interesting find! – Edward Thomson Oct 10 '13 at 17:19
  • Ah, but it looks like it still fails badly whenever rename detection fails. My script just happened to fail rename detection due to empty files, but if you replace "file" with a completely different "File", the new File will be deleted after merging! – Dan Fabulich Oct 10 '13 at 22:51
  • That's a hell of a bug. – Edward Thomson Oct 11 '13 at 00:10
  • @DanFabulich note: git 2.0.1+ should solve that issue: see [my answer below](http://stackoverflow.com/a/24978787/6309) – VonC Jul 27 '14 at 07:20

2 Answers2

1

That issue ("deletes file with renamed capitalization") is precisely what commit ae352c7f37ef2098e03ee86bc7fd75b210b17683 by David Turner (dturner-tw) solves:

merge-recursive.c: fix case-changing merge bug

On a case-insensitive filesystem, when merging, a file would be wrongly deleted from the working tree if an incoming commit had renamed it changing only its case.
When merging a rename, the file with the old name would be deleted -- but since the filesystem considers the old name to be the same as the new name, the new file would in fact be deleted.

We avoid this by not deleting files that have a case-clone in the index at stage 0.

That means that, starting git 2.0.1+ (June 25th, 2014), the merge should now work.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

This problem only occurs when rename detection fails.

http://thread.gmane.org/gmane.comp.version-control.git/235889

It just so happens that git disabled rename detection of empty files in git 1.8. https://github.com/git/git/commit/4f7cb99ada26be5d86402a6e060f3ee16a672f16

This script runs as expected on git 1.7 and git 1.8:

#!/bin/sh -x
# create git repo
git --version
rm -rf caps
git init caps
cd caps
git config --get core.ignorecase
# commit empty file called "file"
echo file > file
git add .
git commit -am "initial commit"
# create branch called "branch"
git branch branch
# rename "file" to "File"
# using --force per http://stackoverflow.com/questions/6899582
git mv --force file File
git commit -am "renamed to File"
# switch to branch, make a non-conflicting commit
git checkout branch
echo newfile > newfile
git add .
git commit -am "branch commit"
# merge master into branch, commit merge
GIT_EDITOR=true git merge --verbose --commit master
ls File
git status

But this script fails, due to a bug in git:

#!/bin/sh -x
# create git repo
git --version
rm -rf caps
git init caps
cd caps
git config --get core.ignorecase
# commit empty file called "file"
echo file > file
git add .
git commit -am "initial commit"
# create branch called "branch"
git branch branch
# rename "file" to "File"
# using --force per http://stackoverflow.com/questions/6899582
git mv --force file File
echo "completely different content" > File
git commit -am "renamed to File"
# switch to branch, make a non-conflicting commit
git checkout branch
echo newfile > newfile
git add .
git commit -am "branch commit"
# merge master into branch, commit merge
GIT_EDITOR=true git merge --verbose --commit master
ls File
git status
Dan Fabulich
  • 37,506
  • 41
  • 139
  • 175