【问题标题】:How to make Git understand that moved file and edited file is the same file?如何让 Git 理解移动的文件和编辑的文件是同一个文件?
【发布时间】:2017-12-12 16:29:22
【问题描述】:

我有 2 个分支。在名为 index.html 的第一个文件中被编辑并提交。在第二个这个文件编辑并移动到其他文件夹。 我可以让 GIT 理解(合并时)第一个分支的 index.html 与第二个分支的文件“folder/index.html”相同。在我看来,问题在于当我将文件移动到新文件夹时,GIT 首先将其删除,然后在新文件夹中创建它。 那么在合并后我将有 1 个包含两个更改的文件?

【问题讨论】:

  • 文件的两个版本是否相同,只是位于不同的文件夹中?
  • 您到底在做什么(您正在运行哪些命令?),您遇到的具体问题是什么?
  • 你想要达到的结果是什么?
  • 现在,合并后我有 2 个文件 index.html。一个在旧文件夹中,另一个在新文件夹中。而且我需要拥有两个分支都有变化的唯一一个。
  • 看起来您可以尝试调整 find-renames 选项(-X find-renames=25% 或其他),但如果这不起作用,您将需要手动合并文件。一旦你这样做了,Git 就没有什么可以记住的了。每个提交都存储为它的树,而不是更改列表,所以一旦你有了合并的文件,这就是你所需要的。

标签: git git-merge


【解决方案1】:

Git 应该已经知道它们是同一个文件。在此处查看此问题的答案。 Is it possible to move/rename files in git and maintain their history?。如果您在移动的文件上键入 git log,您会在移动后看到提交。以下命令应该有完整的历史记录。

git log --follow folder/index.html

【讨论】:

    【解决方案2】:

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


    如果您在自己的分支中有两个 修改过的文件,它们都来自一个单个 源文件,Git 的git merge 将无济于事。您必须自己合并此文件,可能使用git merge-file。有关说明,请参阅下面的最后一节。

    git merge 有三个输入

    这里的根本问题是 git merge 只查看三个提交。1 我们需要这些提交的名称,而 Git 对这些名称并不一致:有时它们是 --ours--theirs 用于两个分支提示提交,有时它们是 localremote。我将使用 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 提交是您在运行 @987654343 时指定的提交@——并且有它的三个输入,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% 相似”运行(您可以在我的其他一些答案中找到此重命名检测器如何工作的详细描述:diff rename, copy, and break detectionsimilarity index),但您可以使用 @ 调整百分比987654350@.

    第二个 git diff 做同样的事情,但他们的 R 提交。

    然后,git merge 继续合并两组差异。

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

    如果没有高级冲突和低级冲突,git merge 通常会立即进行新的提交。您可以使用--no-commit 抑制此操作并对工作树进行额外的工作。 (结果是一些人所说的evil merge。你应该意识到这一点,并且至少以某种方式标记提交,例如,在提交消息中。或者,进行正常合并,然后进行修复提交。没有这个特殊问题的完美答案。)

    手动合并

    要手动合并复制的文件,请提取合并基础版本(您可以使用 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 只是遵循简单的一次一行的规则来组合差异。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-28
      • 2011-05-01
      • 2021-02-12
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      • 2010-11-28
      相关资源
      最近更新 更多