From what I understand, Git doesn't really need to track file rename/move/copy operations, so what's the real purpose of git mv? The man page isn't specially descriptive...

Is it obsolete? Is it an internal command, not meant to be used by regular users?

share|improve this question
up vote 262 down vote accepted
git mv oldname newname

is just shorthand for:

mv oldname newname
git add newname
git rm oldname

i.e. it updates the index for both old and new paths automatically.

share|improve this answer
28  
Also it has a few safeties built in. – Jakub Narębski Jul 8 '09 at 0:16
3  
Thanks @CharlesBailey - Does git then consider the files newNameFile and oldNameFile as different? If yes, what happens if we want to merge them? Say we branch an ant project on branch A and create Branch B then mavenize projects on B. The file names are the same but put on different paths as the project structure changed. Say both branches grew for some time in parallel. At some point if we want to merge the projects how will git know that it's the same file just renamed it's path? (if "git mv" == "git add + git rm") – Rose Dec 27 '12 at 0:22
    
I guess, it is the same thing just with 99.9999% probability. Obviously, the auto-detection could go wrong if you have e.g. multiple files with the same name and/or the same content. – osa Oct 12 '13 at 3:57
1  
@SergeyOrshanskiy If auto detection goes wrong for mv oldname newname; git add newname; git rm oldname, it will also go wrong for git mv oldname newname (see this answer). – Ajedi32 Jun 11 '14 at 21:28
1  
Note that git mv is slightly different from the mv oldname newname; git add newname; git rm oldname, in that if you made changes to the file before git mving it, those changes won't be staged until you git add the new file. – Ajedi32 Jun 11 '14 at 21:31

From the official GitFaq:

Git has a rename command git mv, but that is just a convenience. The effect is indistinguishable from removing the file and adding another with different name and the same content

share|improve this answer
4  
So do you loose the file history? I was presume rename would keep the old history for that directory... – Will Hancock Apr 12 '13 at 9:52
12  
Well, yes and no. Read the official GitFaq link above about renames, and then read Linus Torvalds lengthy e-mail about why he doesn't like the notion of an SCM tool tracking files: permalink.gmane.org/gmane.comp.version-control.git/217 – Adam Nofsinger May 7 '13 at 15:05
1  
@WillHancock I have used git a bit more now, and can answer you more definitively: depending on your git client and its options, you will be able to trace the file past the rename if the file changed internally little enough that it considers it a rename. If you change the file too much AND rename it though, git won't detect it - in a sense it is saying "no, you might as well consider that a completely different file!" – Adam Nofsinger Mar 6 '15 at 22:09
1  
@AdamNofsinger how much exactly is "too much"? If a possible rename is tracked in "search time" as Torvalds has stated, then there must be a clear definition of a rename specific for each client, e.g. 90% of the characters or lines have changed... Is that so? – hosolmaz Sep 23 '15 at 13:46

Git is just trying to guess for you what you are trying to do. It is making every attempt to preserve unbroken history. Of course, it is not perfect. So git mv allows you to be explicit with your intention and to avoid some errors.

Consider this example. Starting with an empty repo,

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
git status

Result:

# On branch master
# 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)
#
#   modified:   a
#   deleted:    b
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   c
no changes added to commit (use "git add" and/or "git commit -a")

Autodetection failed :( Or did it?

$ git add *
$ git commit -m "change"
$ git log c

commit 0c5425be1121c20cc45df04734398dfbac689c39
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

and then

$ git log --follow c

Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

commit 50c2a4604a27be2a1f4b95399d5e0f96c3dbf70a
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:45 2013 -0400

    initial commit

Now try instead (remember to delete the .git folder when experimenting):

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
git mv a c
git status

So far so good:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    a -> c


git mv b a
git status

Now, nobody is perfect:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a
#   deleted:    b
#   new file:   c
#

Really? But of course...

git add *
git commit -m "change"
git log c
git log --follow c

...and the result is the same as above: only --follow shows the full history.


Now, be careful with renaming, as either option can still produce weird effects. Example:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git commit -m "first move"
git mv b a
git commit -m "second move"

git log --follow a

commit 81b80f5690deec1864ebff294f875980216a059d
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:35:58 2013 -0400

    second move

commit f284fba9dc8455295b1abdaae9cc6ee941b66e7f
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:34:54 2013 -0400

    initial b

Contrast it with:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git mv b a
git commit -m "both moves at the same time"

git log --follow a

Result:

commit 84bf29b01f32ea6b746857e0d8401654c4413ecd
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:37:13 2013 -0400

    both moves at the same time

commit ec0de3c5358758ffda462913f6e6294731400455
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:36:52 2013 -0400

    initial a

Ups... Now the history is going back to initial a instead of initial b, which is wrong. So when we did two moves at a time, Git became confused and did not track the changes properly. By the way, in my experiments the same happened when I deleted/created files instead of using git mv. Proceed with care; you've been warned...

share|improve this answer
3  
+1 for the detailed explanation. I've been looking for problems that might happen in log history if files are moved in git, your answer was really interesting. Thank you! Btw, do you know any other pitfalls that we should avoid while moving files in git? (or any reference you could point to.... not very lucky googling for it) – pabrantes Mar 21 '14 at 16:24
1  
Well, my examples are pessimistic. When the files are empty, it is much more difficult to properly interpret the changes. I imagine that if you just commit after every set of renames, you should be fine. – osa Mar 23 '14 at 1:43

As @Charles says, git mv is a shorthand.

The real question here is "Other version control systems (eg. Subversion and Perforce) treat file renames specially. Why doesn't Git?"

Linus explains at http://permalink.gmane.org/gmane.comp.version-control.git/217 with characteristic tact:

Please stop this "track files" crap. Git tracks exactly what matters, namely "collections of files". Nothing else is relevant, and even thinking that it is relevant only limits your world-view. Notice how the notion of CVS "annotate" always inevitably ends up limiting how people use it. I think it's a totally useless piece of crap, and I've described something that I think is a million times more useful, and it all fell out exactly because I'm not limiting my thinking to the wrong model of the world.

share|improve this answer
    
Do you have a mirror for that link? It's broken now. – cjm Aug 26 '16 at 20:28
3  

There's another use I have for git mv not mentioned above.

Since discovering git add -p (git add's patch mode; see http://git-scm.com/docs/git-add), I like to use it to review changes as I add them to the index. Thus my workflow becomes (1) work on code, (2) review and add to index, (3) commit.

How does git mv fit in? If moving a file directly then using git rm and git add, all changes get added to the index, and using git diff to view changes is less easy (before committing). Using git mv, however, adds the new path to the index but not changes made to the file, thus allowing git diff and git add -p to work as usual.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.