git-mv的用途是什么?

bbmckpt7  于 2023-05-05  发布在  Git
关注(0)|答案(9)|浏览(110)

据我所知,Git并不需要跟踪 file rename/move/copy操作,那么git mv的真正目的是什么呢?手册页不是特别描述性的。
它过时了吗?它是一个内部命令,不打算由普通用户使用吗?

n3ipq98p

n3ipq98p1#

git mv oldname newname

只是一种简写

mv oldname newname
git add newname
git rm oldname

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

jyztefdp

jyztefdp2#

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

hts6caw3

hts6caw33#

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时,也发生了同样的情况。小心行事;我警告过你

jdzmm42g

jdzmm42g4#

正如@Charles所说,git mv是一种简写。
这里真实的的问题是“其他版本控制系统(例如。Subversion和Perforce)特别处理文件重命名。为什么Git没有?“
Linus在http://permalink.gmane.org/gmane.comp.version-control.git/217上用特有的机智解释道:
拜托,别再说什么“跟踪档案”了。Git精确地跟踪重要的东西,即“文件集合”。其他的一切都是无关紧要的,甚至认为它是相关的,也只会限制你的世界观。注意CVS“注解”的概念总是不可避免地限制了人们使用它的方式。我认为这是一个完全无用的垃圾,我已经描述了一些我认为有用一百万倍的东西,这一切都失败了,因为我没有把我的思想限制在错误的世界模型上。

ymdaylpp

ymdaylpp5#

有一种特殊的情况,git mv仍然非常有用:当您想要在不区分大小写的文件系统上更改文件名的大小写时。默认情况下,APFS(mac)和NTFS(windows)都不区分大小写(但保留大小写)。
greg.kindel在评论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
3zwtqj6y

3zwtqj6y6#

git mv移动文件,更新索引以记录被替换的文件路径,并更新任何受影响的git子模块。与手动移动不同,它还检测仅大小写的重命名,否则不会被git检测为更改。
它的行为类似于(尽管不完全相同)将文件从外部移动到git,使用git rm从索引中删除旧路径,并使用git add将新路径添加到索引中。

答题动机

这个问题有很多很好的部分答案。这个答案是试图将它们组合成一个单一的有凝聚力的答案。此外,有一件事没有被任何其他答案所提及,那就是man page实际上基本上回答了这个问题,但它可能没有那么明显。

详细说明

手册页中列出了三种不同的效果:
1.文件、目录或符号链接在文件系统中移动:
git-mv -移动或重命名文件、目录或符号链接
1.更新索引,添加新路径并删除前一个路径:
成功完成后将更新索引,但仍必须提交更改。
1.移动的子模块将更新为在新位置工作:
使用gitfile移动子模块(这意味着它们是用Git版本1.7.8或更高版本克隆的)将更新gitfile和core.worktree设置,使子模块在新位置工作。它还将尝试更新gitmodules(5)文件中的子模块. .path设置并暂存该文件(除非使用-n)。
正如this answer中提到的,git mv非常类似于移动文件,将新路径添加到索引中,并从索引中删除以前的路径:

mv oldname newname
git add newname
git rm oldname

然而,正如this answer所指出的,git mv在行为上与此并不完全相同。通过git mv移动文件会将新路径添加到索引中,但不会添加文件中的任何修改内容。另一方面,使用这三个单独的命令会将整个文件添加到索引中,包括任何修改过的内容。当使用修补索引的工作流而不是在文件中添加所有更改时,这可能是相关的。
此外,如this answer和此评论中所述,git mv具有处理case-insensitivecase-preserving文件系统上仅大小写重命名的额外好处,这在当前的macOS和Windows文件系统中经常出现。例如,在这样的系统中,git不会检测到通过mv Mytest.txt MyTest.txt移动文件后文件名发生了变化,而使用git mv Mytest.txt MyTest.txt则会成功更新其名称。

bxfogqkk

bxfogqkk7#

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照常工作。

vhmi4jdf

vhmi4jdf8#

也许git mv已经改变了,因为这些答案被张贴,所以我将简要更新。在我看来,git mv***不***准确地描述为简写:

# not accurate: #
 mv oldname newname
 git add newname
 git rm oldname

我经常使用git mv的原因有两个,在之前的回答中没有描述过:
1.移动大型目录结构,其中我混合了跟踪和未跟踪文件的内容。跟踪和取消跟踪的文件都将移动,并保留其跟踪/取消跟踪状态
1.移动较大的文件和目录时,我总是假设git mv将减少存储库DB历史记录的大小。这是因为移动/重命名文件是索引/引用增量。我还没有证实这个假设,但它似乎是合乎逻辑的。

2fjabf4q

2fjabf4q9#

有一个2023年的讨论主张Git显式记录重命名。
(Note:仍然是一个讨论,当然 * 不是 * 还没有一个功能)。
a comment regarding git mv

git show :0:oldname > newname
git add newname
git rm --cached
oldname mv oldname newname ```

基本上,移动,但也保留暂存/未暂存的内容。
但是,是的。git处理重命名的方式有时候会有点像皮塔。
并且:

git add file1
commit -m "Commit 1"
echo 'Changed' > file1
git mv file1 file2
git status

其产生:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    file1 -> file2

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   file2

相关问题