【问题标题】:Git: Conflicts merging multiple repositories into one directory keeping the historyGit:将多个存储库合并到一个目录中保留历史记录的冲突
【发布时间】:2017-10-24 21:20:43
【问题描述】:

我有几个 (~20) 个 Git 存储库,它们的文件不重叠。我想将他们的主分支合并到一个(新)存储库中。

经过一番阅读,我想出了以下过程。

  1. 创建目标存储库 (git init) 并更改为它
  2. git remote add <name> <url>
  3. git fetch <name>
  4. git merge <name>/master --allow-unrelated-histories -m "Imported"
  5. git remote rm <name>
  6. 重复 2-5 直到合并所有存储库

第一个存储库合并得很好,历史记录完好无损,但后来我遇到了合并冲突。

例如对于不同目录中具有相同名称的不同文件(并且我这边没有重命名):

CONFLICT (rename/rename): Rename "Splittermond_CharGen_JFX/.project"->"BootloaderPlugin/.project" in branch "HEAD" rename "Splittermond_CharGen_JFX/.project"->"Splittermond_Zhoujiang/.project" in "splimo-common/master"

例如来自我在项目历史中移动的文件(树中留下的版本是最近的位置):

CONFLICT (rename/delete): Splittermond_BuU/src/org/prelle/rpgframework/splittermond/buu/BestienUndUngeheuerPlugin.java deleted in HEAD and renamed to Splittermond_BuU/src/main/java/org/prelle/rpgframework/splittermond/buu/BestienUndUngeheuerPlugin.java in splimo-common/master. Version splimo-common/master of Splittermond_BuU/src/main/java/org/prelle/rpgframework/splittermond/buu/BestienUndUngeheuerPlugin.java left in tree.

我认为 Gits 跟踪文件的能力可能是问题所在,但我对此很陌生,不知道如何解决这个问题。

感谢任何帮助或提示。

[更新] 看起来我至少有两个存储库 - 虽然不再重叠 - 曾经重叠。我有一个 git repo A,它抱怨它删除了现在在 repo B 中的文件。我有一个 repo B,它曾经包含现在在 repo A 中的文件。 有没有办法合并两者,保留所有未删除文件的历史记录?

【问题讨论】:

    标签: git merge merge-conflict-resolution


    【解决方案1】:

    重命名检测可能会产生错误的结果。然后你可以尝试通过在合并命令中添加-Xno-renames 来避免它:

    ...
    git merge <name>/master --allow-unrelated-histories -m "Imported" -Xno-renames
    ...
    

    这应该不会造成伤害,因为您正在合并不相关的历史并且不要期望任何重命名。

    【讨论】:

    • 我明天会尝试报告。
    • 这很有帮助,但并没有完全解决。使用此选项可以消除(重命名/重命名)冲突。 (重命名/删除)冲突仍然存在。仍然:谢谢你的提示。
    【解决方案2】:

    我完全不清楚发生了什么,我需要访问有问题的存储库和您的命令来重现它。但是,在考虑如何解决这一切时,这里有两个关键点需要牢记:

    • 在 Git 中,历史 提交(或者,更明确地说,“提交就是历史”)。如果您想保留历史记录,这意味着您想保留现有的提交。
    • Merging(动词形式,to merge)在 Git 中意味着在两个(可能很长的)提交链之间找到一个共同的基本提交,以便比较“什么我们在我们的分支上做了”到“他们在他们的分支上做了什么”因为那个共同点

    正常的合并有两个“方面”。我称它们为 L 表示左侧、本地或 --ours,而 R 表示右侧、远程或 --theiRs。它也有这个合并基础提交,这是我们和他们在我们开始做自己的事情之前开始的共同点。 Git 通过运行将“我们做了什么”与“他们做了什么”结合起来:

    git diff --find-renames B L   # base to left/local: what we did
    git diff --find-renames B R   # base to right: what they did
    

    如果,例如,“我们”和“他们”都修改了相同文件的相同行,或者我们添加了一个文件path/to/new.txt,他们添加了相同的path/to/new.txt,但它具有不同的内容,则会发生合并冲突,或者我们删除了path/to/old.txt,他们修改了path/to/old.txt

    当您使用--allow-unrelated-histories 时,您是在告诉 Git,如果没有共同的提交(这在此处通常是正确的),Git 应该假装有一个共同的基础,该基础由一个没有文件。也就是说,对于两个git diff 命令中的B,Git 应该替换the empty tree,这样每个文件都是新的。

    现在,你说:

    ... [相当多的] Git 存储库在其文件中不重叠

    如果是这种情况,那么 LR 中都不能有 path/to/new.txt。如果两边都有新文件,则文件重叠。

    此外,如果历史记录确实不相关,您不能遇到重命名/重命名或重命名/删除冲突,因为不会有合并基础并且 Git 将为 使用空树B 每次。您遇到这种冲突的事实表明历史 相关,因此 Git 找到了一个共同的合并基础,而来自该共同合并基础的 git diff 在一侧找到了重命名操作,并且在另一侧进行不同的重命名或删除。

    正因为如此,如何更轻松地合并所有这些不相关的历史的显而易见的答案无法使用:有些文件重叠的,这种方法不会那么容易工作。但如果它们真的完全不重叠,那么合并它们的方法是从所有存储库中获取所有提交,然后构建一个主“章鱼合并”提交(这里我使用 merge 作为形容词或名词,而不是动词),其树是通过在所有适当的分支提示上使用 git read-tree -m 生成的以建立合并索引,并且其提交是通过运行 git write-tree 然后 git commit-tree 生成的(带有适当的标志)。

    不过,我很犹豫是否提供此方法,因为如果它要工作,您确实需要不相关的输入,而您看到的失败告诉我您没有不相关的输入。

    【讨论】:

    • repos 源自单个 Subversion 存储库,我将其转换为多个 Git 存储库,方法是先构建单个 Git 存储库,然后将子目录移动到不同的存储库中。所以他们曾经在很短的时间内拥有一个共享存储库,但它们仍然不应该重叠。谢谢你的解释。我将进一步探索并报告。
    • 我认为我们已经接近了这里的问题。看起来我至少有两个存储库 - 尽管不再重叠 - 一个已经重叠。我有一个 git repo A 抱怨它删除了现在在 repo B 中的文件。我有一个 repo B,它曾经包含现在在 repo A 中的文件。有没有办法合并两者,保留所有文件的历史记录没有被删除?
    • 我真的必须查看存储库(或足够好的传真)才能理解问题并设计答案。请注意,Git 通过图遍历找到合并基,因此重要的是提交哈希。
    • 这可能有助于清理存储库的历史记录以从同时已删除的所有文件中导入。这个可以吗?
    • 您可以按目录拆分(请参阅git subtree),或删除特定文件(使用树或索引过滤器和filter-branch)以及--prune-empty 标志以使Git 省略提交从他们的父母那里没有改变。至少可以说,这很麻烦,而且可能不是正确的方法,尽管这在很大程度上也取决于您未来的计划。
    【解决方案3】:

    感谢toreks 的帮助,我想出了以下解决方案:

    1. 使用git init 初始化新存储库
    2. git remote add &lt;name&gt; &lt;url&gt;
    3. git fetch &lt;name&gt;
    4. git merge &lt;name&gt;/master --allow-unrelated-histories -m "Reimported"
    5. git remote rm &lt;name&gt;
    6. git ls-files &gt; /tmp/keep-these.txt
    7. git filter-branch --force --index-filter "git rm --ignore-unmatch --cached -qr . ; cat /tmp/keep-these.txt | xargs git reset -q \$GIT_COMMIT --" --prune-empty --tag-name-filter cat -- --all
    8. 对每个存储库重复步骤 2-7

    添加的步骤 6-7 取自 new-repo-with-copied-history-of-only-current-tracked-files

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-17
      • 1970-01-01
      • 2017-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      相关资源
      最近更新 更多