【问题标题】:Merge changes of copied repository without true common ancestor in git在 git 中合并没有真正共同祖先的复制存储库的更改
【发布时间】:2016-06-24 21:03:42
【问题描述】:

我有一个项目 DemoA,它是基于 git 存储库 Project1 构建的。

不幸的是,DemoA 最初只是从 Project1 中复制的文件,然后才变成一个实际的长期项目。我现在想让 Project1 成为 DemoA 的子模块,但更重要的是,我希望在 DemoA 中合并对从 Project1 派生的代码所做的更改。

我在 DemoA 上进行了子树拆分,以创建一个分支 P1,其中包含对 DemoA 中的 Project1 代码库所做的所有更改。

在将 DemoA 实例化为存储库之前,我还设法添加了对 Project1 所做的更改。

Project1
A - B - C - D                     - E
Demo1/P1
    (untracked changes) F - G - H - I

where the files in E are identical to F

我想要什么:

Project 1
A - B - C - D - E - G - H - I

显然 E 和 F 的哈希值不同,所以当我将 Demo1/P1 作为远程添加到 Project1 并尝试合并时,它抱怨没有共同的祖先。

我试过using format-patch,但是 git am 抱怨了

错误:file.xyz:索引中已存在

我正在尝试rebase onto a different branch,正在做:

git rebase -s recursive -X subtree=project1dir --onto (E-hash) (F-hash) emptybranch

但我显然不明白它实际上在做什么,因为它似乎并没有真正任何事情。

有没有一种干净的方法可以做到这一点?我不介意这个过程有些手动,但我想保留历史。

【问题讨论】:

    标签: git version-control merge


    【解决方案1】:

    这都是中等难度(实际难度级别取决于环境和您对 Git 的熟悉程度)。

    如果 EF 中的文件确实相同,那么(或)简单的方法是放入一个移植(使用git replace 或移植文件),以便 Git 假装G 的父提交是提交 E。也就是说,你有:

    A--B--C--D--E   <-- master
    
    F-------------G--H--I   <-- refs/remotes/rem/P1
    

    git diff master rem/P1~4 根本不产生任何输出(master 名称提交 Erem/P1~4 名称提交 FEF 的两棵树完全匹配)。

    希望,至少作为一个中间产品,你有这个:

    A--B--C--D--E   <-- master
                 \
    F             G--H--I   <-- refs/remotes/rem/P1
    

    也就是说,您希望 Git 至少在某些目的和一段时间内假装提交 G 具有提交 E 作为其父项。

    使用 git replace 模拟旧的可怕的黑客移植

    Git 移植正是这样做的:它们告诉 Git 假装某个提交的父级是另一个提交。但这些已被弃用,取而代之的是更通用的git replace。您可以使用git replace 进行新的提交G',它类似于(但至少对于大多数Git 命令而言取代)G,不同之处在于G' 具有E 作为其父级。

    然后您可以使用git filter-branch 重新复制存储库中的提交,以便此替换成为真实且永久的,而不仅仅是副本。当然,您将获得新提交的新提交哈希(G' 可以保留其哈希,但您必须获得新的 H'I')。请参阅 this answer by Jakub Narębski,然后是 How do git grafts and replace differ? (Are grafts now deprecated?),其中 VonC 链接到 Jakub 的答案。

    (Git 移植仍然有效,您可以将提交 GE 的哈希值放入 .git/info/grafts: echo $(git rev-parse rem/P1~3) $(git rev-parse master) &gt; .git/info/grafts,例如。但它们 太可怕了hack,如果你做了这种技巧,最好在之后立即运行你的过滤器分支,正如 Jakub 所说。)

    使用git rebase

    您也可以使用git rebase --onto,就像您尝试的那样,但是您必须使用指向提交的现有(普通的本地)分支名称(我不确定emptybranch 来自哪里)启动此变基I。我认为您可能缺少的步骤可能是使这个常规的普通本地分支名称:

    git checkout -b rewrite rem/P1
    

    例如,假设名称 rem/P1 解析为提交 I。或者git checkout -b rewrite &lt;hash-of-I&gt;,如果你面前有那个哈希以便于剪切/粘贴。那时你会得到这个:

    A--B--C--D--E   <-- master
    
    F-------------G--H--I   <-- HEAD -> rewrite, rem/P1
    

    也就是说,你现在在这个新的rewrite 分支上,它指向提交I。现在您可以git rebase --onto master HEAD~3 复制当前分支上最近的 3 次提交——GHI。副本将是G'H'I',其中G' 的父级是E——master 指向的提交——而H' 的父级是G' 和以此类推:

                  G'-H'-I'   <-- HEAD -> rewrite
                 /
    A--B--C--D--E   <-- master
    
    F-------------G--H--I   <-- rem/P1
    

    现在您可以删除远程及其远程跟踪分支,因为您拥有所需的提交链。你也可以在任何时候快进master 指向提交I',如果这是你想要的。

    【讨论】:

    • 对不起,我没有时间尝试这个;但看起来它可以解决我的问题!我会在不久的将来尝试并接受它是否有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多