git-mv的目的是什么?


287

据我了解,Git确实不需要跟踪文件重命名/移动/复制操作,那么git mv的真正目的是什么?手册页不是特别描述性的...

它过时了吗?它是内部命令,不适合普通用户使用吗?

Answers:


390
git mv oldname newname

只是以下内容的简写:

mv oldname newname
git add newname
git rm oldname

即,它会自动更新旧路径和新路径的索引。


38
此外它内置了一些安全装置。
的JakubNarębski

6
谢谢@CharlesBailey-git是否会将文件newNameFile和oldNameFile视为不同的文件?如果是,如果我们要合并它们会怎样?假设我们在分支A上分支了一个ant项目,然后在分支B上创建了一个分支,然后在B上进行了项目修饰。文件名相同,但是随着项目结构的改变,它们的路径不同。假设两个分支同时增长了一段时间。在某个时候,如果我们要合并项目,git怎么会知道它是刚刚重命名为路径的同一文件?(如果“ git mv” ==“ git add + git rm”)
2012年

2
@SergeyOrshanskiy如果自动检测错误mv oldname newname; git add newname; git rm oldname,则也将错误git mv oldname newname(请参见此答案)。
Ajedi32

5
请注意,git mv与稍有不同mv oldname newname; git add newname; git rm oldname,如果您在更改文件之前git mv对其进行了更改,则只有在您创建git add新文件后,这些更改才会被暂存。
Ajedi32

2
git mv正在做一些不同的事情,因为它处理文件名大小写的更改(从foo.txt到Foo.txt),而那些单独运行的命令则不(在OSX上)
greg.kindel

66

来自官方的GitFaq

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


8
那么,您会丢失文件历史记录吗?我以为重命名会保留该目录的旧历史记录...
Will Hancock

17
好吧,是的,不是。阅读上面有关重命名的官方GitFaq链接,然后阅读Linus Torvalds冗长的电子邮件,以了解为什么他不喜欢SCM工具跟踪文件的概念:permalink.gmane.org/gmane.comp.version-control.git/ 217
亚当·诺夫辛格

3
@WillHancock我现在更多地使用了git,并且可以更明确地回答您:根据您的git客户端及其选项,如果文件内部更改很小,以至于您认为它是一个文件,您将能够跟踪该文件通过重命名。改名。如果您对文件进行了太多更改并进行了重命名,则git不会检测到它-在某种意义上说“不,您不妨考虑一个完全不同的文件!”
亚当·诺夫辛格

7
@AdamNofsinger链接已死。这是一面镜子:web.archive.org/web/20150209075907/http
卡尔·沃尔什

2
是否有正式参考(例如,比FAQ更有价值)指出git mv了手动方法之间的等效性?从中不明显git help mv
tvo

40

Git只是想为您猜测您要做什么。它正在尽一切努力来保存不间断的历史。当然,这并不完美。因此git mv,您可以明确自己的意图并避免一些错误。

考虑这个例子。从一个空的仓库开始,

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
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 a c
git status

到目前为止,一切都很好:

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


git mv b a
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显示完整的历史记录。


现在,在重命名时要小心,因为这两个选项仍然会产生怪异的效果。例:

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

与之对比:

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

结果:

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感到困惑,并且无法正确跟踪更改。顺便说一句,在我的实验中,删除/创建文件而不是使用时也发生了同样的情况git mv。谨慎行事;您已经被警告...


5
+1为详细说明。我一直在寻找如果在git中移动文件可能会在日志历史记录中发生的问题,您的回答确实很有趣。谢谢!顺便说一句,您知道在git中移动文件时应避免的其他陷阱吗?(或您可以指向的任何参考....对此不是很幸运的谷歌搜索)
pabrantes 2014年

1
好吧,我的例子是悲观的。当文件为空时,正确解释更改要困难得多。我想如果您只是在每组重命名之后提交,那应该没问题。
osa

27

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

这里真正的问题是“其他版本控制系统(例如Subversion和Perforce)特别对待文件重命名。为什么Git不?”

莱纳斯(Linus)在http://permalink.gmane.org/gmane.comp.version-control.git/217中解释了具有典型特征的内容:

请停止此“跟踪文件”废话。Git 精确地跟踪重要的内容,即“文件集合”。闲来无事是相关的,甚至是 思维,这是相关的限制了你的世界观。请注意,CVS“注释”的概念总是不可避免地最终会限制人们的使用方式。我认为这完全是没用的废话,我已经描述了一些我认为有用的东西一百万倍,并且全部落空的原因 恰恰是因为我没有将我的思维局限于错误的世界模型。


您是否有该链接的镜像?现在坏了。
cjm


9

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

自发现以来git add -p(git add的补丁程序模式;请参阅http://git-scm.com/docs/git-add),在将更改添加到索引时,我喜欢用它来查看更改。这样,我的工作流程就变成了(1)处理代码,(2)查看并添加到索引,(3)提交。

如何git mv适应?如果直接移动文件,则使用git rmgit add,所有更改都将添加到索引中,并且使用git diff查看更改不太容易(在提交之前)。git mv但是,使用会为索引添加新路径,但不会对文件进行更改,从而允许git diffgit add -p照常工作。


5

在一个特殊情况下,它git mv仍然非常有用:当您想在不区分大小写的文件系统上更改文件名的大小写时。默认情况下,APFS(mac)和NTFS(windows)都不区分大小写(但保留大小写)。

greg.kindel在评论CB Bailey的答案时提到了这一点。

假设您正在Mac上工作,并且有一个Mytest.txt由git管理的文件。您想要将文件名更改为MyTest.txt

您可以尝试:

$ mv Mytest.txt MyTest.txt
overwrite MyTest.txt? (y/n [n]) y
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

噢亲爱的。Git不承认文件有任何更改。

可以通过完全重命名文件然后将其重命名来解决此问题:

$ mv Mytest.txt temp.txt
$ git rm Mytest.txt
rm 'Mytest.txt'
$ mv temp.txt MyTest.txt
$ git add MyTest.txt 
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt

欢呼!

或者,您可以使用以下方法节省所有麻烦git mv

$ git mv Mytest.txt MyTest.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.