如何让 Git 理解移动的文件和编辑的文件是同一个文件



我有2个分支。在第一个名为 index 的文件中.html被编辑并提交。其次,此文件已编辑并移动到其他文件夹。 我可以让 GIT 理解(合并时)来自第一个分支的索引.html是第二个分支的相同文件"folder/index.html"。在我看来,问题是当我将文件移动到新文件夹中时,GIT 首先删除了它,然后在新文件夹中创建了它。 所以合并后我会有 1 个文件和两个更改?

Git 应该已经知道它们是同一个文件。 请在此处查看此问题的答案。 是否可以在 git 中移动/重命名文件并维护其历史记录? 如果在移动的文件上键入 git log,则会看到移动后的提交。 以下命令应具有完整的历史记录。

git log --follow folder/index.html

编辑:我现在相信我今天早上误读了这个问题,你的分支中只有一个修改的文件,来自单一来源。 在这种情况下,下面的描述是准确的,但告诉我们 Git 应该自己找到重命名的文件,只要它足够相似。 如果没有,您可以添加和调整-X find-renames=number参数以git merge。 您可能希望先运行一些手动git diff,使用--find-renames=number(或较短的拼写,-Mnumber)来找出哪些数字使 Git 看到合并基础--ours更改为重命名。


如果你在自己的分支中有两个修改的文件,它们都派生自一个源文件,Git 的git merge不会帮助你。 您将不得不自己合并此文件,也许使用git merge-file. 有关说明,请参阅下面的最后一部分。

有三个输入要git merge

这里的根本问题是git merge只查看三个提交。1我们需要这些提交的名称,而 Git 对这些名称并不是非常一致:有时它们是两个分支提示提交的--ours--theirs,有时它们是本地远程的。 我将使用L表示左侧、本地或--ours,R表示右侧、远程或--theirs。 第三次提交在某些方面是最重要的,但 Git 会自行找到第三次提交:我们无法控制它选择哪个提交。 我们只选择LR提交。 第三个提交是合并基础,简而言之,2它是两个分支首先重新组合在一起的地方:

o--...--o--o   <-- L
/
...--o--*

o--o--...--o   <-- R

或:

...--o--o--o--*--o   <-- L
     
o--o--o--o   <-- R

在这两个图中,标记为*的提交是合并基础。


1为简单起见,我们在这里假设一个正常的合并,两个头,一个合并基提交。 具有两个或多个合并基地的十字交叉合并案例,以及具有两个以上头部的章鱼合并,使情况复杂了很多,但无论如何都不是我们关心的情况。

2这有点短了,但在大多数情况下已经足够了。


Git 做了两个差异

现在 Git 已经找到了合并库——L提交只是我们当前的分支提示,而 R提交是你在运行git merge时指定的那个——并且有三个输入,Git 运行git diff两次:

git diff --find-renames base L   # what we did
git diff --find-renames base R   # what they did

第一个git diff将存储在合并基础提交中的所有文件(请记住,每个提交都是所有文件的快照)与存储在 L中的所有文件进行比较,以查看我们在--ours中更改了哪些内容。--find-renames选项使此特定git diff检查已重命名的文件,但不检查已复制的文件。 重命名检测器以默认的"至少 50% 相似"运行(您可以在我的其他一些答案中找到有关此重命名检测器如何工作的详细说明:差异重命名、复制和中断检测和相似性指数),但您可以使用-X find-renames=n调整百分比。

第二个git diff执行相同的操作,但使用其R提交。

然后,git merge继续将这两组差异结合起来。

如果两个差异中的一个或两个检测到基index.html在一个或两个LR提交中被重命名为其他路径,Git 将采用其中一个重命名(如果这两个提交都重命名了基本文件,则声明高级重命名/重命名冲突)。 不过,同样,没有打开复制检测的选项,Git 只会将任何副本视为LR中的一个或两个中的新文件。 如果两者都添加了,这可能会导致高级添加/添加冲突;或者,如果它只是在一个提交中添加,Git 假设它应该添加到新提交中。

如果没有高级别冲突和低级别冲突,git merge通常会立即进行新的提交。 您可以使用--no-commit来抑制这种情况,并对工作树执行其他工作。 (结果就是一些人所说的邪恶合并。 您应该意识到这一点,并至少以某种方式标记提交,例如,在提交消息中。 或者,进行正常合并,然后进行修正提交。 这个特定问题没有完美的答案。

手动合并

要手动合并复制的文件,请提取合并基版本(可以找到带有git merge-base的合并基提交哈希 ID),并将两个提示版本提取为三个普通文件,然后对其使用git merge-file。 请注意,默认情况下,git merge-file将其输出写入三个输入之一 - 这可能是您想要的,因为 HEAD 或--ours版本已经在您的工作树中,在您用于它的名称下,因此您真正要做的就是提取基本版本和--theirsR版本:

git show base-hash:base-path > newname.base
git show theirs:theirs-path > newname.theirs
git merge-file newname newname.base newname.theirs

然后在完成合并后删除.base.theirs版本。 (与往常一样,检查生成的合并是明智的,因为 Git 只是遵守简单的一次一行规则来组合差异。

最新更新