首页 > 解决方案 > 如果整个文件夹结构发生更改,git 如何管理更改?

问题描述

我正在处理分支,我最近重构了我的文件夹结构,很多文件被移动到这里和那里,许多文件也被重命名。但是当我将 master(旧结构)合并到我当前的分支时,git 能够理解文件的位置并自动合并代码而不会发生冲突。这怎么可能?

标签: git

解决方案


这个“如何”有两个部分,可以这样概括:

  • 对于提交,Git不在乎。它只是制作快照。无论你告诉 Git 使用什么文件名来保存你告诉 Git 使用的任何文件内容——所有这些文件在你运行时都存储在你的索引中git commit,这就是为什么你必须一直保存git add文件,以便将它们复制到旧的索引中的版本——运行git commit. 那些冻结的文件是提交的快照。

    换句话说,在您重新排列所有内容git mv(更改索引和工作树中的名称)并git add根据需要更新内容之后git commit,您将获得包含新名称和任何更新内容的新快照。旧的快照保持原样:所有现有的快照都被永远冻结,或者至少在提交本身存在的时候。(默认设置是它们永远存在。可以删除提交,但这只是简单的,直到您将它们传播到其他存储库,之后即使您删除它们,它们也会继续从其他存储库重新感染您的存储库来自你自己的。)

  • 为了比较——包括合并——Git 必须发现/检测重命名的文件。Git 通过比较文件的内容来做到这一点。

这里第二个要点中的声明实际上有点夸张,但是让我们通过用 来说明它是如何工作的git diff,它——至少在我们在这里关心的模式下——比较了两个提交。请记住,每个提交都代表所有文件的完整快照。我们将找到两个提交的哈希 ID,然后运行:

git diff --find-renames <hash of earlier commit> <hash of later commit>

此时 Git 将做的是提取两个提交中的每一个。(“提取”尽可能地短路,这通常很多:Git 通常可以直接检查冻结的提交。但这只是速度优化;你可以认为这是 Git 将两个提交完全提取到一个临时工作区。)让我们在这里将较早的提交称为的,将后的提交称为的。1 的工作git diff是告诉您如何将旧的更改为新的。这不一定是任何进行更改的人所做的,只是一些会产生相同结果的指令。

要找到这些说明,Git 将:

  • 首先,在oldnew中找到所有同名的文件。Git 假设如果old有一个名为 的文件,READMEnew有一个名为 的文件README,那么它们必须是“相同的”文件。这些文件是配对的:它们现在被排除在等式之外,目前。Git 还没有弄清楚如何更改配对文件,它只是将它们配对。

  • (您可以在此处使用该-B选项将一个步骤插入到具有该选项的命令中。但我们现在将忽略它,因为它只会使这变得复杂。)

  • 现在,如果有未配对的文件,这些文件代表旧文件丢失和/或文件突然出现......还是他们?也许它们是被重命名的文件,在old中有一些名称 O ,在new中有一些不同的名称 N 。在这里,Git为每个可能的文件对计算相似性索引号。

    出于速度目的,Git 可以非常快速地配对任何两个100% 相同的文件(一个来自old,一个来自new )。这通常会大大缩小必须进行硬比较的文件池。

    最后,Git 归结为未配对的文件,即使它们不是 100%,位对位相同,它也应该考虑配对。Git 现在对每对文件进行完整的相似性索引计算(使用Git 用于在包文件中进行 delta 压缩的类似 xdelta 的代码)。(这需要真正提取所有这些文件的数据。)如果分数超过您选择的最小值,则将获得最佳配对分数的文件配对在一起,默认为“50% 相似”。

  • 在完成所有这些额外工作后仍未配对的文件要么被删除,要么被重新创建。--find-copies(如果您添加or ,这里会引入一些更复杂的情况--find-copies-harder,但同样,我们将在这里忽略它们。)

现在文件已经配对了——也就是说,现在git diff知道了,比如说,README.md文件中的文件大多与文件中的文件匹配,因此这两个文件必须是一个文件,具有一个身份,而不是两个不同的文件两个身份——<em>现在 Git 会比较每个配对文件以生成指令:README.rst

  • -:从旧文件的版本中删除这一行
  • +:将此行添加到旧文件的版本中

如果您遵循所有说明,包括顶部给出的任何“重命名此文件”说明,则会将old中找到的文件更改为new中找到的文件。


1如果愿意,您可以反转哈希 ID。然后 Git 会告诉你如何将较新的提交转换为较旧的提交。


如何git merge使用git diff

当合并两个提交时,Git 使用提交图——通过查看每个提交的父哈希或哈希将所有单独的提交连接到一个大 DAG 中形成的有向无环图——来找到最佳的共同祖先提交。此提交是两个指定提交的合并基础

然后,该git merge命令实际上运行了两个 git diff命令。两者都已--find-renames启用,相似度阈值默认为 50%。您可以使用-X find-renames=<number>更改此阈值,以允许更多或更少名称不匹配的文件配对。

这两个差异是:

git diff --find-renames <hash of merge base> <hash of HEAD commit>

和:

git diff --find-renames <hash of merge base> <hash of other commit>

两个 diff 在内部git diff.

并发症

添加-B告诉 Git打破自动配对的文件:仅仅因为一个文件README在两个提交中都被命名并不意味着这真的是同一个文件。例如,如果您重命名READMEold/README,然后重命名new/READMEREADME怎么办?在这种情况下,Git 将在我之前提到的那个步骤中对自动配对的文件进行相似度计算。如果相似度太低,Git 会破坏配对。稍后,如果相似度不是太低并且配对仍然断开,Git 将重新加入这两个文件,因此-B需要两个数字,而不仅仅是一个。

合并命令不允许您提供-B参数。(可以说,它应该。)

如果您使用--find-copiesor --find-copies-harder,Git 将查看部分或全部源(“旧”)文件,以查看是否从中复制了新创建的目标(“新”)文件。这些使用相同的相似性指数。此步骤发生在重命名检测之后,并且有时只会将修改后的文件视为可能的来源,因为它的计算成本很高。

合并命令也不允许您指定查找副本选项。


推荐阅读