Dan Fabulich
Dan Fabulich

Reputation: 39603

On OSX, git merge deletes file with renamed capitalization

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.)

Upvotes: 4

Views: 513

Answers (2)

VonC
VonC

Reputation: 1329812

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.

Upvotes: 1

Dan Fabulich
Dan Fabulich

Reputation: 39603

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

Upvotes: 0

Related Questions