Git错误地推断“重命名”

5
Git在我没有这样做的情况下推断出了“重命名”(这个问题实际上是 如何让git将删除的文件和新文件标记为文件移动? 的相反):
  1. 我创建了一个新文件,并执行了 git add 。(我还没有进行 git commit ,因为我现阶段没有这样的愿望。)
  2. 稍后我在另一个文件上执行了 git rm
  3. 现在 git status 报告 重命名:旧文件 -> 新文件 。 我还没有 commit
这两个文件在同一目录下,名称相似且有一定的共同内容。但是,我故意没有使用git mv,因为这不是一次重命名,我希望这两个文件可以分别被跟踪。如果我想要重命名,我会使用git mv而不是我的故意的git add/git rm
是什么导致git认为这是一次重命名的操作?是否可以告诉它不要尝试推断我不想要的东西?

你能否在你的问题中提供确切的步骤以便重现吗? - ckruczek
我以为这就是我的编号为1/2/3的步骤! - JonBrave
是的,没错,但也许你可以为我们创建一个简单、可重现的示例,包括类似的文件内容等。 - ckruczek
3个回答

4

Git的逻辑底层存储模型仅在更改前后存储库内容,而不是更改本身。因此,它无法区分例如移动+修改和删除+添加之间的区别。

因此git mv只是方便语法, 相当于:

mv a b
git rm a
git add b

git status仅仅是根据变更前后状态推断出最有可能的变更原因,以使人类可读的输出更加有用。当然,在某些情况下可能存在异常情况——在您的特定情况下,它推断出变更是由移动和微小内容更改引起的。

基于评论讨论的更新:如果您需要明确这里发生了什么,可以(如您建议的)将addrm分别作为两个提交来进行。但这样做将把一个“逻辑”的提交分成两个,尽管这可能不重要。


1
你认为类似的内容可能与此有关吗? - Tim Biegeleisen
@JonBrave - 啊,我漏了那个。我会很快更新我的答案,但基本故事仍然相同 - git status 推断出观察到的变化最可能的原因。 - Oliver Charlesworth
我认为git status报告的内容确实会在我执行git commit/push时被记录下来。如果是这样的话,答案应该说明我可以采取什么措施来避免这种情况发生,例如,如果我在add之后、rm之前执行了git commit,那么是否可以防止这种不良行为的发生? - JonBrave
1
@JonBrave - 我想关键点在于,就Git而言,这些是无法区分的 - 提交/推送将是相同的。当然,你可以使用一些技巧来影响人类可读的日志输出,但通常会牺牲其他方面(例如,在你的示例中,需要两个提交而不是一个)。 - Oliver Charlesworth

2
文件非常相似,以至于 git status 认为这是一次重命名。实际上,这并没有任何区别,但是如果您希望确保不会发生这种情况,请分别提交添加和删除文件的提交记录:
git add newfile
git commit
git rm oldfile
git commit

在我的情况下,很有可能是在我创建git add之前的newfile时,我将旧文件的内容复制到了新文件中,因此它们是相同或非常相似的。为了避免额外的“commit”,如果在未来我首先将newfile设置为空,然后进行git add,然后再将旧内容复制到其中,这样是否可以避免“重命名”推断,从而避免需要中间的“commit”,或者推断不是最初进行的,而是根据以后的内容动态进行的? - JonBrave
2
正如Oliver Charlesworth所说,Git会动态地进行重命名检测,只要它比较两个提交并且您已启用重命名检测(--find-renames[=<number>])。在Git中,默认情况下禁用重命名检测,除了git mergegit status之外。自Git 2.12左右开始,默认情况下启用重命名检测。可选数字是相似性指数,介于0到100之间。100表示文件必须完全相同。请注意,您可以为git diffgit merge设置此值,但git status将其硬编码为50%。 - torek
@torek 哦,这个 --find-renames 很有趣!但可能不太适合,因为它必须在 commit/push 时启用/禁用,并且可能会作用于所有文件,而我想在 add/rm 阶段一次性完成此操作。你还说 git status 可能会报告与在 commit/push 时实际决定的不同(因为 --find-renames),对吗?你可以在自己的解决方案帖子中解释这个方面,因为这可能对其他阅读此问题的人有所帮助。 - JonBrave
1
重命名检测对Git存储的内容没有任何影响,只是改变了它所显示的内容。尝试提交您所拥有的内容,然后运行 git diff --find-renames=1 HEAD^ HEADgit diff --find-renames=100 HEAD^ HEADgit diff --no-renames HEAD^ HEAD。请注意不同的输出,但每次提交都完全相同!(请记住,每个提交都是所有文件的独立快照。像 git show 这样的命令通过将提交与其父提交进行比较来工作:它们运行 git diff,因此允许您指定重命名检测的启用/级别。) - torek
@torek 啊,所以“重命名”在存储库中并不是真正的存储方式(我以为它会这样),它只是一个“动态报告提示”,在查看git版本历史树时不会显示为此类信息?如果是这样的话,我认为你可能需要发布一篇文章来解释这个问题和--find-renames,这是非常有用但不明显的信息。 - JonBrave
显示剩余2条评论

1
Git会在检测到类似内容的删除和添加时(默认相似度为50%)推断重命名,无论历史记录中哪对提交之间计算差异。也就是说,Git不会将删除和添加记录为重命名,因此使用git rm ...git add ...或者git mv ...(这实际上是另外两个命令的别名)没有区别。
Git会推断重命名,除非你告诉它不要这样做。但要注意:如果你告诉Git不推断重命名,则它将不会推断任何重命名,即使你想将某些删除和添加配对为重命名。
如需了解更多详细信息,建议阅读gitdiffcore的文档。

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