首页 > 解决方案 > 重命名文件与拉取请求压缩合并

问题描述

据我所知,当您在一次提交中提交重命名/移动并在另一次提交中进行修改时,git 可以处理文件重命名/移动。

但是当你压缩合并你的 PR 分支时,它是如何工作的呢?

IE

  1. 在您的功能分支中:重命名文件并提交
  2. 在您的功能分支中:修改文件并提交
  3. 创建公关
  4. 壁球合并到主人

与此同时,其他人在他们自己的功能分支中修改了相同的文件。

当他们将 master 合并到他们的特性分支时会发生什么?据我了解,重命名和文件修改在主服务器的同一提交中(由于壁球合并),对吗?那么 git 将如何处理呢?

标签: gitgit-merge-conflict

解决方案


据我所知,当您在一次提交中提交重命名/移动并在另一次提交中进行修改时,git 可以处理文件重命名/移动。

嗯,是的,但也不是。

这里的关键是 Git 不存储更改。Git 存储快照。换句话说,每个提交都有一份完整且完整的源代码副本。如果你重命名一堆文件,不做任何其他更改,然后创建一个新快照,旧快照具有旧名称下的内容,新快照具有相同的内容,但在一组新名称下。

如果您重命名和修改文件,旧快照在旧名称下具有旧内容,而新快照在新名称下具有新内容。

Git 的作用git log --followgit diff --find-renames拍摄两个快照——让我们说一个有趣的事件“之前”和“之后”,在这种情况下,有趣的事件是重命名——然后比较它们。在内部,提交的文件存储为 <name, hash-ID> 对(或更准确地说,<mode, name, hash-ID> 三元组):

$ git ls-tree -r HEAD
[snippage]
100644 blob 41b718c29e1b9fc2981d7d14a3d25e69c31a3030    version.c
100644 blob 7c62e80577154d79bec050424945eb500d262a0f    version.h
100644 blob 069ee94a4d79422ea659a7ebe3923662f0626afa    versioncmp.c
100644 blob bb010f7a2b3c1090bc9c62f613cede7bbda86e97    walker.c
[snippage]

这里blob实际上是部分mode100644始终是 blob 对象)的文本表示,因此这些行中的每一行都是读取条目的结果,其中包含此名称和哈希 ID 配对。

每个文件内容的哈希 ID 仅基于文件数据,而不是文件。例如,无论文件名为walker.c还是funny.name,只要内容相同,hash ID也将相同。

因此,给定左侧和右侧快照(之前和之后),如果哈希 ID匹配,则内容也匹配。这使得查找git diff --find-renames重命名变得非常快:我们只需将所有匹配的哈希 ID 对齐,左侧的名称就会重命名为右侧的名称。

如果文件稍作修改,这种快速重命名检测技巧将不起作用。现在 Git 必须实际提取所有左侧文件和所有右侧文件,并比较它们。与快速“查看哈希 ID”技巧不同,这实际上非常困难(要配对的文件数量为O(n 2 ))。Git 将首先尽力配对所有左侧和右侧文件而不检查其内容,以便“可能重命名”文件的列表尽可能小,然后只查看那些不是已经配对了。

因此,Git可以处理这两种情况——只要内容没有太大变化,相似度检测器可以消耗大量 CPU 时间来通过相似度索引匹配文件——但它需要的计算能力要少得多如果git diff两个提交的差异事件只是一个重命名事件,则进行匹配重命名查找。这意味着所有的哈希 ID 都匹配,而快速匹配代码完成了整个工作。

但是当你压缩合并你的 PR 分支时,它是如何工作的呢?

它没有。

壁球合并是工具。在适当的时候使用它们。当壁球合并不合适时,请使用其他工具。

(请记住, squash-merge 意味着run git merge,但随后将结果作为普通提交而不是合并提交提交。在命令行中,每次都git merge --squash好像您还包含该选项一样,因此您必须自己运行命令. GitHub clicky "squash and merge" 按钮不使用命令行命令,所以它有点不同,但你得到的最后一组提交与你在命令行上完成所有这些是一样的。)--no-commitgit commit


推荐阅读