据我所知,Git并不真正需要跟踪文件重命名/移动/复制操作,那么真正的目的是什么呢 git mv?手册页没有特别的描述性……
它过时了吗?它是一个内部命令,而不是供普通用户使用的吗?
据我所知,Git并不真正需要跟踪文件重命名/移动/复制操作,那么真正的目的是什么呢 git mv?手册页没有特别的描述性……
它过时了吗?它是一个内部命令,而不是供普通用户使用的吗?
当前回答
正如@Charles所说,git mv是一个简写。
这里真正的问题是“其他版本控制系统(例如。Subversion和Perforce)专门处理文件重命名。为什么Git不呢?”
Linus在http://permalink.gmane.org/gmane.comp.version-control.git/217上用他特有的机智解释道:
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.
其他回答
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
...结果与上面相同:only——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时,也发生了同样的情况。小心地进行;我警告过你……
也许git mv在这些答案发布后发生了变化,所以我会简单更新一下。在我看来,git mv不能准确地描述为:
# not accurate: #
mv oldname newname
git add newname
git rm oldname
我经常使用git mv有两个原因,在之前的回答中没有描述:
移动大型目录结构,其中我混合了跟踪和未跟踪文件的内容。被跟踪和未跟踪的文件都将移动,并保持其跟踪/未跟踪状态 移动较大的文件和目录,我一直认为git mv将减少存储库DB历史大小。这是因为移动/重命名文件是索引/引用增量。我还没有证实这个假设,但它似乎是合乎逻辑的。
在一个特殊的情况下,git mv仍然非常有用:当您想在不区分大小写的文件系统上更改文件名的大小写时。默认情况下,APFS (mac)和NTFS (windows)都是不区分大小写的(但保留大小写)。
格雷格。金德尔在对CB Bailey的回答的评论中提到了这一点。
假设你在mac上工作,git管理着一个文件Mytest.txt。您需要将文件名更改为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
正如@Charles所说,git mv是一个简写。
这里真正的问题是“其他版本控制系统(例如。Subversion和Perforce)专门处理文件重命名。为什么Git不呢?”
Linus在http://permalink.gmane.org/gmane.comp.version-control.git/217上用他特有的机智解释道:
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.
git mv还有一个上面没有提到的用途。
自从发现git add -p (git add的补丁模式;参见http://git-scm.com/docs/git-add),我喜欢在将更改添加到索引时使用它来查看更改。因此,我的工作流程变成了(1)处理代码,(2)检查并添加到索引中,(3)提交。
git mv是如何适应的?如果直接移动文件,那么使用git rm和git add,所有的更改都会被添加到索引中,而使用git diff查看更改就不那么容易了(在提交之前)。然而,使用git mv会将新路径添加到索引中,但不会对文件进行更改,因此允许git diff和git add -p照常工作。