I'd read that when renaming files in git, you should commit any changes, perform your rename and then stage your renamed file. Git will recognise the file from the contents, rather than seeing it as a new untracked file, and keep the change history.
However, doing just this tonight I ended up reverting to git mv
.
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
Rename my stylesheet in Finder from iphone.css
to mobile.css
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: css/iphone.css
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# css/mobile.css
So git now thinks I've deleted one CSS file, and added a new one. Not what I want, lets undo the rename and let git do the work.
> $ git reset HEAD .
Unstaged changes after reset:
M css/iphone.css
M index.html
Back to where I began.
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
Lets use git mv
instead.
> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: css/iphone.css -> css/mobile.css
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: index.html
#
Looks like we're good. So why didn't git recognise the rename the first time around when I used Finder?
you have to
git add css/mobile.css
the new file andgit rm css/iphone.css
, so git knows about it. then it will show the same output ingit status
you can see it clearly in the status output (the new name of the file):
and (the old name):
i think behind the scenes
git mv
is nothing more than a wrapper script which does exactly that: delete the file from the index and add it under a different nameYou didn't stage the results of your finder move. I believe if you did the move via Finder and then did
git add css/mobile.css ; git rm css/iphone.css
, git would compute the hash of the new file and only then realize that the hashes of the files match (and thus it's a rename).For git 1.7.x the following commands worked for me:
There was no need for git add, since the original file (i.e. css/mobile.css) was already in the committed files previously.
Let's think about your files from git perspective.
Your repository has (among others)
and it is under git control:
Test this with:
When you do
From git perspective,
So, git advises about files it already knows (iphone.css) and new files it detects (mobile.css) but only when files are in index or HEAD git starts to check their contents.
At this moment, neither "iphone.css deletion" nor mobile.css are on index.
Add iphone.css deletion to index
git tells you exactly what has happened: (iphone.css is deleted. Nothing more happened)
then add new file mobile.css
This time both deletion and new file are on index. Now git detects context are the same and expose it as a rename. In fact if files are 50% similar it will detect that as a rename, that let you change mobile.css a bit while keeping the operation as a rename.
See this is reproducible on
git diff
. Now that your files are on index you must use--cached
. Edit mobile.css a bit, add that to index and see the difference between:and
-M
is the "detect renames" option forgit diff
.-M
stands for-M50%
(50% or more similarity will make git express it as a rename) but you can reduce this to-M20%
(20%) if you edit mobile.css a lot.In cases where you really have to rename the files manually, for eg. using a script to batch rename a bunch of files, then using
git add -A .
worked for me.