git-mv的目的是什么?

据我所知,Git并不需要跟踪文件重命名/移动/复制操作,那么git mv的真正目的是什么? 手册页不是专门描述的

它是否过时? 这是一个内部的命令,不打算被普通用户使用吗?

git mv oldname newname 

只是简写:

 mv oldname newname git add newname git rm oldname 

即自动更新旧path和新path的索引。

从官方的GitFaq :

Git有一个重命名命令git mv ,但这只是一个方便。 效果与删除文件和添加另一个具有不同名称和相同内容的区别无法区分

Git只是试图猜测你正在尝试做什么。 它正在尽一切努力保持不间断的历史。 当然,这并不完美。 所以git mv可以让你明确你的意图并避免一些错误。

考虑这个例子。 从一个空的回购开始,

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

结果:

 # 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") 

自动检测失败 :(或做到了?

 $ 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 

接着

 $ 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 

现在试试(记住在试验时删除.git文件夹):

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

到现在为止还挺好:

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

现在,没有人是完美的:

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

真? 但是当然…

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

…结果与上面相同:只有 – --follow显示完整的历史logging。


现在,要小心重命名,因为任何一个选项仍然可以产生奇怪的效果 。 例:

 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 ac git commit -m "first move" git mv ba 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 

与以下对比:

 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 ac git mv ba git commit -m "both moves at the same time" git log --follow a 

结果:

 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 …现在历史回到最初的a,而不是最初的b ,这是错误的。 所以当我们一次做了两个动作的时候,Git变得困惑,没有正确地跟踪这些变化。 顺便说一下,在我的实验中,当我删除/创build文件,而不是使用git mv时,也发生了同样的情况。 谨慎行事; 你已经被警告过了…

正如@Charles所说, git mv是一个简写。

这里真正的问题是“其他版本控制系统(例如Subversion和Perforce)专门处理文件重命名,为什么不Git?

莱纳斯在http://permalink.gmane.org/gmane.comp.version-control.git/217上解释说:;

请停止这个“跟踪文件”废话。 Git 完全跟踪重要的内容,即“文件集合”。 没有什么是相关的,甚至认为相关只会限制你的世界观。 注意CVS的概念“注释”总是不可避免地最终限制了人们使用它的方式。 我认为这是一个毫无用处的东西,而且我描述了一些我认为有用的东西,这完全是因为我没有将自己的思想局限于世界的错误模式。

上面没有提到的git mv还有另一个用途。

由于发现git add -p (git add的补丁模式;请参阅http://git-scm.com/docs/git-add ),我喜欢用它来查看更改,因为我将它们添加到索引。 因此,我的工作stream成为(1)代码工作,(2)审查和添加到索引,(3)提交。

git mv如何适应? 如果直接移动文件,然后使用git rmgit add ,所有更改都会被添加到索引中,并且使用git diff来查看更改并不容易(提交之前)。 然而,使用git mv会将新path添加到索引,但不会更改该文件,从而允许git diffgit add -p像往常一样工作。