如何告诉Git,这是同一个目录,只是名称不同

35

在学习Git时,我克服了一些困难,但又遇到了一个新挑战:重命名目录(在本地工作目录中)。

当我输入git status时,它会列出旧目录名称中的所有文件(这些文件与新目录中完全相同的文件名存在),状态为已删除,而新目录名称为“未跟踪”。

是否有一种方法可以告诉Git,“实际上是相同的目录,只是名称不同”?

这样所有的文件就只会被 git status 列出为修改过的文件了吗?

为了举例说明问题,这里是当我重命名整个目录时从git status接收到的输出:

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)
#
#   deleted:    old-dir-name/file1
#   deleted:    old-dir-name/file2
#   deleted:    old-dir-name/file3
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   new-dir-name/
no changes added to commit (use "git add" and/or "git commit -a")
~/sb/ws>

2
我刚刚测试了一下,我没有遇到那个问题。在我的电脑上,Git成功地识别出我重命名了一个文件夹,并继续跟踪它的所有文件,而不会在git status中报告任何内容。 - karlphillip
1
@karlphillip 你是怎么重命名文件夹的?在我的情况下,我完全删除了它,并从不同的源复制了相同的内容但使用不同的名称。这会有什么影响吗? - WinWin
2
很可能。我只是用 mv 重命名了它:mv folder new_name。Git 足够聪明,可以检测到您是创建了新文件还是仅重命名了一个文件夹。但是当您删除目录并创建一个新目录(即使它具有先前目录的相同文件)时,Git 认为所有这些文件都是新文件。将来,只需使用 mv 进行重命名即可。如果您喜欢,我可以将此作为答案添加进去。 - karlphillip
在 Git 2.18(2018 年第二季度)中,git status 现在应该会显示重命名文件(而不是删除/添加文件)。请参见下面的我的回答 - VonC
5个回答

20

只需要在新名称下使用git add添加目录即可。Git并不显式地跟踪重命名,它稍后会检测到它们。使用git mv可能会略微更加高效(因为它可以直接更新索引),但效果完全相同。


这正是我的理解,也是我提出问题的原因:有没有办法告诉Git追踪重命名?这样,如果我使用gitk来比较旧名称和新名称之间的差异,我就能够做到了吗? - WinWin
2
@WinWin:这取决于工具本身。例如,git diff命令有一个-M选项,允许你指定文件匹配的程度以显示重命名操作。 - hammar
@hammar 那是个很好的选择,我不知道它是否也适用于 git statusgit commit - WinWin
@WinWin:git status 不支持此操作。对于 git commit 来说也没有太多意义。我猜你想要的是 -a,但这不起作用。只需使用 git add 添加新/移动的目录即可。任何能检测到重命名的工具都会将其视为重命名。Git 不知道其中的区别。 - hammar

17

所有这里的答案都对我遇到的特定场景的实际解决方案非常有帮助。我将提供针对我的情况起作用的实际步骤,希望能帮助遇到相同挑战的其他人:

  1. git mv <old-dir-name> <new-dir-name>
  2. git status (确认所有文件标记为renamed,而不是“deleted”)
  3. git commit -a -m "git mv <old-dir-name> <new-dir-name>" (这是一个“虚假的”提交,为下一步进行真正的重命名做准备)
  4. git branch git_mv_20110708_1500_DO_NOT_USE (“虚假”的分支,带有时间戳,提醒我们只是以此作为解决方法的临时解决方法)
  5. /bin/rm -Rf <new-dir-name>
  6. cp -Rp .../<new-dir-name> . (用重命名后的名称复制实际文件夹)
  7. git status (大多数修改过的文件现在会正确标记为modified,而不是“deleted”。已重命名的文件将被标记为已删除并且已添加,尽管内容相同! - 如果需要对它们进行重命名跟踪,则重复步骤1-7)
  8. git add <untracked files>
  9. git commit -a -m "finally renamed this folder"
  10. git branch FOLDER_RENAMED :)

P.S. gitk 喜欢这个,但是 Emacs 对重命名仍然感到困惑。 :(


有点混乱,在我的情况下,我的文件夹被索引为小写字母,但实际上它是大写字母,所以有效的方法是删除它及其子文件夹,提交更改,创建新文件夹并复制所有子文件夹,再次提交更改。 - kafinsalim

7
你需要使用git的mv命令:http://www.kernel.org/pub/software/scm/git/docs/git-mv.html
git mv old_dir new_dir

所以你需要将新目录移回旧目录,然后使用mv重新移动它。
编辑:为了回答我的答案的回复:
我决定自己测试一下。我创建了两个具有不同文本的文件,并在其中一个上使用了git mv,而在另一个上使用了mv file file2、git add file2、git add -u。提交消息表明两者都被跟踪为重命名。因此,我的所有建议只是节省了一步,正如其他人所说的那样。

7
这相当于对旧文件名执行 git rm,对新文件名执行 git add。Git 跟踪的是内容而非变化。当你要求查看变化时,变化会被即时推断出来。 - hammar
从这里的理解是,git mv只是一个简写,结果与问题相同:如果我使用gitk在新版本和新名称之间进行差异比较,我无法这样做,因为它们被列为两个不同的文件。 - WinWin
尝试使用 git diff -M 命令,查看它是否能检测到重命名。 - Phil Miller

2
Git 2.18 (Q2 2018)应该能够检测到“它是同一个目录,只是不同的名称”,因为"git status"学会了关注与UI相关的差异配置变量,如diff.renames。请参见dc6b1d9提交(2018年5月4日),作者是Eckhard S. Maaß (``)(由Junio C Hamano -- gitster --1e174fd提交中合并,于2018年5月23日)

wt-status:使用git_diff_ui_config中的设置

如果您执行以下操作:

- git add .
- git status
- git commit
- git show (or git diff HEAD)

one would expect to have analogous output from git status and git show (or similar diff-related programs).
This is generally not the case, as git status has hard coded values for diff related options.

With this commit the hard coded settings are dropped from the status command in favour for values provided by git_diff_ui_config.

What follows are some remarks on the concrete options which were hard coded in git status:

`diffopt.detect_rename`

Since the very beginning of git status in a3e870f ("Add "commit" helper script", 2005-05-30, Git v0.99), git status always used rename detection, whereas with commands like show and log one had to activate it with a command line option.
After 5404c11 ("diff: activate diff.renames by default", 2016-02-25, Git v2.9.0) the default behaves the same by coincidence, but changing diff.renames to other values can break the consistency between git status and other commands again.
With this commit one control the same default behaviour with diff.renames.

`diffopt.rename_limit`

Similarly one has the option diff.renamelimit to adjust this limit for all commands but git status. With this commit git status will also honor those.

Git 2.18版本提供了status.renames,对于那些想在不禁用"git diff"命令默认重命名检测的情况下进行重命名检测的人来说,这可能很有用。
请参见commit e8b2dc2(由Ben Peart (benpeart)在2018年5月11日提交)。
(于2018年5月30日合并至commit 5da4847,由Junio C Hamano -- gitster --合并)

为重命名检测添加状态配置和命令行选项

在执行具有冲突的合并后,默认情况下,git status将尝试检测重命名,这会导致许多对象被检查。
在虚拟化存储库中,这些对象不存在于本地,因此重命名逻辑会触发从服务器获取这些对象。这导致状态调用在非常大的存储库上需要花费几个小时,而使用此补丁只需几秒钟。

添加一个新的配置项status.renames,以启用在状态和提交期间关闭重命名检测。
此设置将默认为diff.renames的值。

添加一个新的配置项status.renamelimit,以便在状态和提交期间限制查找不精确的重命名所花费的时间。
此设置将默认为diff.renamelimit的值。

向状态添加--no-renames命令行选项,以启用从命令行覆盖配置设置。
添加--find-renames[=<n>]命令行选项到状态,以启用检测重命名并可选择设置相似性索引。


-1

这个问题是一个用户界面/用户体验问题,而不是git的“真正”问题。我已经问过同样的问题如何在git中处理子目录重命名和一个基本问题git如何记录或更可能表示其blob的文件路径和名称

实际上,Git并不关心这些。你(我们)得到的所有消息都是关于“diff”认为可能发生了什么。使用其他选项,消息将不同,但存储库不会有任何不同(它是相同的快照!)。

一种选项风格是diff的subtree选项(尽管它是为不同的目的),还有--patience

需要一个更好的UI/UX选项,可以更轻松地检测路径更改(基于树而不是blob)。问题的一部分在于Linus的论点中存在微妙的错误。他是正确的,Git存储库不应明确存储重命名(路径更改),而是我们需要一个选项来允许其正确发现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接