【问题标题】:Git Moving a Branch to Another One with One Commit HistoryGit 将一个分支移动到另一个具有一个提交历史的分支
【发布时间】:2017-01-17 13:38:39
【问题描述】:

我正在我的功能分支上进行开发。开发完成后,我会将所有提交的代码移动到 master 分支。

但是具体一点是在这个操作之后主分支应该只有一个提交历史。

我会尝试用树来解释。

--- BRANCH TREES WITHOUT MERGE OPERATION ---

A--B--C  (master-branch)
 \    ^--- After the merge operation commit will come after this point.
  \
   D--E--F--G  (feature-branch)


--- AFTER THE MERGE OPERATION EXPECTED BRANCH TREES ---

A--B--C--(H)  (master-branch)
 \        ^--- 'H' is the commit of all feature branch merge. 
  \             Include commits D, E, F, G.  
   \            This should have all source but, will shown one commit   message.
    \
     D--E--F--G  (feature-branch)

我怎样才能像这样合并这些分支?

【问题讨论】:

  • 迁移到master意味着合并到master分支
  • @PankajBadukale 这不仅仅意味着 master 分支。它也可以是另一个功能分支。
  • 您要更改feature-branch 原点(提交A)。如果是,则在变基过程时进行壁球提交。如果没有将feature-branch 合并到master 将导致一个合并提交

标签: git branch git-branch git-merge


【解决方案1】:

我认为您想要做的是“壁球提交”功能。

我只是复制原始分支,然后以交互方式对其进行变基,这样我就可以压缩提交。

【讨论】:

    【解决方案2】:

    Git 将此称为“壁球合并”,您可以通过运行git merge --squash 来获得它。与 Git 中的所有新提交一样,新提交将添加到当前分支中,因此在这种情况下,您将首先进入 master-branch,然后运行 ​​git merge --squash feature-branch

    注意事项

    对此有一些重要的事情需要了解。特别是,虽然与结果提交相关联的 tree(源或工作树)是通过 merging(动词:动作,“合并”)获得的,但它是不是合并提交(合并的形容词或名词形式)。

    请记住,在 Git 中,提交有两个功能:

    • 他们历史。

    • 它们包含工作树快照(加上一些元数据:提交者、提交时间和日志消息)。

    普通的git merge other 通过运行git merge-base --all HEAD other1 找到一个合并基提交,然后:

    • 结合两组差异:从基础到 HEAD 的差异,相当于“你改变了什么”,加上从基础到 other 的差异,相当于“他们改变了什么”。如果您更改了文件one.txt,而他们更改了文件two.txt,Git 会同时进行这两项更改,即您的one.txt 和他们的two.txt。如果您更改了文件three.txt,Git 会尝试查看您的更改和它们的更改是否重叠,或者完全重复,或者完全独立。如果它们重叠,Git 会给你一个合并冲突。如果它们是独立的,Git 会同时进行这两项更改。如果它们是完全重复的,Git 会复制一份更改。生成的three.txt三路合并的输出。

    • 创建一个新的提交,其历史说:“这个提交是一个合并提交,结合了第一个父级HEAD和第二个父级other的工作”。

    git merge --squash other 仍然执行合并操作,包括任何所需的三向合并。然后 - 没有明显的原因2 - 省略了git commit 步骤并让你运行git commit,当你运行git commit时,结果是不是合并提交:它的历史表明“这个提交是一个普通的提交,它的唯一父级是HEAD”。换句话说,它的提交历史就像是您自己对other 进行了所有更改,除了任何重复的更改。

    这是你所要求的,但它也是一个陷阱。如果您稍后决定需要在 feature-branch 上进行更多开发并再次合并,Git 将不会有显示现有合并的历史记录,并且不会知道它可以使用以下命令启动新的合并过程上一次合并的结果。

    这最终意味着,当您打算在 squash-merging 之后立即(或至少很快)删除功能分支时,squash 合并效果最好:

    A---B----C      <-- main
     \
      D--E--F--G    <-- feature
    

    (现在git checkout main &amp;&amp; git merge --squash feature &amp;&amp; git commit &amp;&amp; git branch -D feature

    A---B----C--H   <-- main
     \
      D--E--F--G    [abandoned]
    

    这样,您不能返回并添加更多的提交过去 G 使用 D-E-F-G 链稍后需要再次合并。如果您确实返回并进行此类提交,然后再次合并,则合并基础提交将是 A,而不是 G,并且 Git 更有可能错误合并,不小心复制了一些 D-E-F-G 更改。 3


    1如果git merge-base --all 找到多个合并基,则行为取决于合并策略。如果它发现 no 合并基础,则旧版本 Git 和新版本 Git 中的行为不同:较新的 Git 会因拒绝合并不相关的历史记录而停止。

    2常规合并会停止,但只有在--no-commit 明确请求时才会停止。随后的git commit 将进行合并提交,即双亲提交。因此,git merge --squash 没有理由表现得好像您还指定了 --no-commit:如果您真的想要它停止,您可以运行 git merge --squash --no-commit。 (这很可能是最早的 Git 脚本的遗留物。)

    3Git 的合并算法会努力仅获取每个更改的副本,但通常会成功仅获取更改的副本。也就是说,从 AG 之后的任何内容的差异包括所有 D-E-F-G 更改,并且从 AH 或之后的任何内容的差异包括所有D-E-F-G 更改:但这就是“获取更改的副本”意味着要处理的内容。

    问题在于,当你对这个算法施加太大压力时,它就会失败。通常,结果是一个巨大的合并冲突,您必须手动解决。

    【讨论】:

    • 非常感谢您的详细回答 :) 这正是我所需要的。
    【解决方案3】:

    其中一种方法:

    git checkout -b tmp feature-branch
    git reset A --soft
    git commit -m "feature"
    #a new commit X is created. It's a squash of D, E, F, G
    git checkout master-branch
    git cherry-pick X
    git branch -D tmp
    

    【讨论】:

      猜你喜欢
      • 2018-05-29
      • 2011-05-06
      • 1970-01-01
      • 1970-01-01
      • 2016-03-23
      • 1970-01-01
      • 2020-06-03
      • 2018-08-12
      • 2015-08-08
      相关资源
      最近更新 更多