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

Posted

技术标签:

【中文标题】如何告诉 Git 它是同一个目录,只是名称不同【英文标题】:How to tell Git that it's the same directory, just a different name 【发布时间】:2011-10-01 11:52:37 【问题描述】:

在克服了学习 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>

【问题讨论】:

我刚刚测试了这个,我没有那个问题。在我的盒子上,Git 成功识别出我重命名了一个文件夹并继续跟踪它的所有文件,而不会在 git status 上报告任何内容。 @karlphillip 你是如何重命名文件夹的?就我而言,我完全删除了它并从不同的来源复制了相同的内容但名称不同。这有什么不同吗? 可能。我只是用 mv 重命名了它:mv folder new_name。 Git 足够聪明,可以检测您是否创建了新文件或只是重命名了文件夹。但是当您删除目录并创建一个新目录时(即使它具有与前一个目录相同的文件),git 认为所有这些文件都是新的。以后只需将其重命名为mv。如果您喜欢,我可以将其添加为答案。 使用 Git 2.18(2018 年第二季度),git status 现在应该向您显示重命名(而不是删除/添加文件)。见my answer below 【参考方案1】:

你需要使用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 file2git add file2git add -u。提交消息表明两者都被跟踪为重命名。因此,正如其他人所说,我的所有建议都是节省一步。

【讨论】:

这相当于只对旧文件名使用git rm,对新文件名使用git add。 Git 跟踪内容,而不是更改。当您要求更改时,会按需推断更改。 @d_r_w 我对this 的理解是git mv 只是导致相同问题的简写:如果我使用gitk 来区分新版本和新名称,我可以'不是因为它们被列为两个不同的文件。 试试git diff -M,看看它检测到重命名。【参考方案2】:

只需git add 使用新名称的目录即可。 Git 不会显式跟踪重命名,它只是稍后检测它们。 git mv 执行起来可能效率稍高一些(因为它可以直接更新索引),但效果完全一样。

【讨论】:

这正是我的理解,也是我问的原因:有没有办法告诉 Git 进行重命名?这样如果我使用gitk 来区分旧名称和新名称,我就可以这样做? @WinWin:这取决于工具。例如,git diff 有一个 -M 选项,允许您指定文件必须匹配多少才能显示为重命名。 @hammar 这是一个不错的选择,我不知道。 git statusgit commit 也可以使用吗? @WinWin: git status 似乎不支持它。 git commit 没有多大意义。我猜你会想要它用于-a,但这行不通。只需git add 新/移动目录。任何检测重命名的工具都会将其检测为重命名。 Git 不知道其中的区别。【参考方案3】:

这里的所有答案对于我所面临的特定场景的实际解决方案都非常有帮助。我以实际步骤的形式提供对我有用的方法,希望能帮助遇到同样挑战的其他人:

    git mv <old-dir-name> <new-dir-name> git status(验证所有文件 标记为重命名,而不是“已删除”) git commit -a -m "git mv <old-dir-name> <new-dir-name>" (这是一个“假”提交,准备 在下一个真正的重命名 步骤) git branch git_mv_20110708_1500_DO_NOT_USE (带有时间戳的“假”分支 提醒我们这样做只是为了 解决方法) /bin/rm -Rf &lt;new-dir-name&gt; cp -Rp .../&lt;new-dir-name&gt; .(复制 在实际文件夹上 改名) git status(修改最多的文件 现在将正确标记为 修改,而不是“删除”。已重命名的文件将 标记为已删除已添加, 尽管内容相同! -- 如果这些文件也需要重命名跟踪,则对这些文件重复步骤 1-7) git add &lt;untracked files&gt; git commit -a -m "finally renamed this folder" git branch FOLDER_RENAMED :)

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

【讨论】:

有点令人困惑,在我的情况下,我的文件夹被索引为小写,但实际上它是大写的,所以可行的是删除它的子级,提交,创建新文件夹并复制所有子级,再次提交。 【参考方案4】:

问题是用户界面/用户体验问题,而不是 git 的“真正”问题。我问了同样的问题how-to-work-with-subdirectory-renames-in-git 和一个潜在的问题how-does-git-record-or-more-likely-represent-file-paths-and-names-for-its-blob

事实上,Git 并不关心。您(我们)收到的所有消息都是关于“差异”认为可能发生的事情。使用其他选项,消息会有所不同,但存储库不会有任何不同(它是相同的快照!)。

一种选项样式是用于 diff 的 subtree 选项(尽管它用于不同的目的),--patience 也是如此。

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

【讨论】:

【参考方案5】:

Git 2.18 (Q2 2018) 应该检测“它是同一个目录,只是名称不同”,因为“git status”学会了关注 UI 相关的差异配置变量,例如 diff.renames

参见Eckhard S. Maaß (``) 的commit dc6b1d9(2018 年 5 月 4 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 1e174fd,2018 年 5 月 23 日)

wt-status:使用来自git_diff_ui_config 的设置

如果你这样做:

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

人们期望git statusgit show(或类似的差异相关程序)有类似的输出。 通常情况并非如此,因为git status 对 diff 相关选项具有硬编码值。

通过此提交,硬编码设置将从状态中删除 支持git_diff_ui_config 提供的值的命令。

以下是对具体选项的一些评论 编码在git status:

`diffopt.detect_rename`

自从a3e870f("添加"提交" 辅助脚本”,2005-05-30,Git v0.99),git status 总是使用重命名检测, 而使用 show 和 log 之类的命令必须使用 命令行选项。 在5404c11 ("diff: 通过激活diff.renames default", 2016-02-25, Git v2.9.0) 巧合,默认行为相同,但将diff.renames 更改为其他值可能会破坏两者之间的一致性 git status 和其他命令再次。 通过此提交,可以控制与 diff.renames 相同的默认行为。

`diffopt.rename_limit`

同样有一个选项 diff.renamelimit 来调整这个限制 除 git status 之外的所有命令。有了这个提交 git status 也将兑现 那些。


同样的 Git 2.18 提供 status.renames,这对于那些想要这样做而不禁用由 “git diff”命令。

见Ben Peart (benpeart)commit e8b2dc2(2018 年 5 月 11 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 5da4847,2018 年 5 月 30 日)

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

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

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

添加一个新的配置status.renamelimit 设置以启用限制时间 花在状态和提交期间找出不准确的重命名。 此设置将默认为 diff.renamelimit 的值。

--no-renames 命令行选项添加到启用覆盖 从命令行配置设置。 将--find-renames[=&lt;n&gt;] 命令行选项添加到启用检测重命名和可选设置相似性索引的状态。

【讨论】:

以上是关于如何告诉 Git 它是同一个目录,只是名称不同的主要内容,如果未能解决你的问题,请参考以下文章

我如何告诉 Git 忽略除子目录之外的所有内容?

如何将 Git 补丁应用于具有不同名称和路径的文件?

Git如何克隆远程仓库

如何在 Windows 7 中删除 git 本地 repo 文件夹

如何在git中合并具有不同目录层次结构的两个分支?

Git入门到高级系列2-git高级操作