合并提交时如何避免rebase地狱?

Alp*_*pha 5 git rebase git-rebase git-rewrite-history git-branch-sculpting

我的git树中有以下情况:

1 -- 2 -- 3 -- 4 <-- master
      \         \
       5 -- 6 -- 7 -- 8 -- 9 <-- feature
Run Code Online (Sandbox Code Playgroud)

我想从功能中重新定义和压缩所有东西,这样我就可以通过添加功能的单个提交来提升master.

由于提交7已经是解决所有冲突的合并,我尝试了以下方法:

git rebase -i -p master
Run Code Online (Sandbox Code Playgroud)

我在这里给出的唯一选择是提交7,8和9."有道理",我想,"因为合并已经包括5和6,所以它们可以被丢弃".我继续在一次提交中压缩7,8和9,我们称之为"789".(我知道,我是创意类型.)

在此之后,我的树看起来像这样:

1 -- 2 -- 3 -- 4 <-- master
                \
                 5 -- 6 -- 789 <-- feature
Run Code Online (Sandbox Code Playgroud)

同一分支中5和6的存在使我感到困惑,但同样,因为它们已经包含在7中(现在是789),我可以放弃它们.

所以我git rebase -i master又一次,这次我丢弃了5和6.

然而,冲突在这里和那里出现,所以我放弃了整个事情.

我目前处于该阶段,但我的远程分支尚未更新,因此我可以重置为原始状态.

在没有手动解决所有合并冲突的情况下,哪些是正确的步骤,这将使我想到的地方?

Aje*_*i32 6

我想从功能中重新定义和压缩所有东西,这样我就可以通过添加功能的单个提交来提升master.

这正是--squash选项的git merge作用.从git merge文档:

- 壁球

生成工作树和索引状态,好像发生了真正的合并(合并信息除外),但实际上没有提交或移动HEAD,也没记录$ GIT_DIR/MERGE_HEAD导致下一个git commit命令创建合并承诺.这允许您在当前分支之上创建单个提交,其效果与合并另一个分支(或章鱼的情况下更多)相同.

(强调我的)

用法示例:

git checkout master
git merge feature --squash
git commit
Run Code Online (Sandbox Code Playgroud)


小智 5

替代Ajedi32的解决方案

到目前为止,Ajedi32的解决方案绝对是最好和最简单的解决方案,但为了完整起见,我只想展示一种方法,它的工作方式大致相同.该--squash选项git merge基本上复制您正在合并的分支的最终提交的整个状态,因为每个提交代表整个快照1.

您可以通过简单地将currnt工作副本目录树替换为正在合并的分支中的最终提交的工作树来实现相同的效果(这可能仅在功能分支包含所有主分支更改时才有效,我是不确定,我将不得不考虑更多).所以你可以做到

git rm -rf -- .
git checkout feature -- .
git add . # Is this necessary?
git commit

# Verify that the current working copy state
# is identical to the feature branch's
git diff feature
Run Code Online (Sandbox Code Playgroud)

1另一种思考方式是将基础分支尖端合并到分支尖端的所有差异的总和.

替代解决方案2(这是我的老答案)

好的,有两件事:

  1. 我有一个解决方案应该为你工作.
  2. 解释为什么你目前所做的不起作用可能是错误的.

一个办法?(这实际上会起作用吗?)

我们将部分修改您的功能分支.当那些合并提交的父母不包括你想要在其他分支上提交的提交时,这个--preserve-merges标志git rebase最有效,因为我相信你已经看过了.

为了简洁起见,我将省略关于以下命令的语法的一些解释,因为您可以在文档中查看:

  1. 首先,做一个备份分支以防万一事情发生可怕,可怕的错误,我们最终需要硬重置回你的功能分支原始状态:

    git branch backup feature
    
    Run Code Online (Sandbox Code Playgroud)
  2. 接下来,我们将压缩5和6,然后樱桃挑选它们master(你也可以将它们重新加入master,但是因为我们只是挑选一个提交,所以rebase对此有点过度杀戮):

    git checkout -b temp 6
    git reset --soft HEAD^
    git commit -m "Enter your squash commit message here"
    
    Run Code Online (Sandbox Code Playgroud)

    现在我们已经将5和6压在一起,我们想要将它挑选出来master.由于您提到合并提交7解决了冲突,这意味着挑选新的压缩提交(让我们称之为S)也会导致冲突.但是,既然你已经解决了这些冲突7,我们应该能够将这些解决方案复制到新挑选的提交中(尽管我对此并不是100%肯定):

    git checkout master
    git cherry-pick S
    
    # If no conflicts, then great!
    # Otherwise, completely delete the conflicted working directory tree,
    # and replace it with the directory tree from commit 7 instead:
    git rm -rf -- .
    git checkout 7 -- .
    git add . # Is this necessary?
    git cherry-pick --continue
    
    # When the cherry-pick is done, compare master to 7 and
    # verify that there are no differences
    git diff 7
    
    Run Code Online (Sandbox Code Playgroud)
  3. 接下来,我们将重新定义8和9.我们跳过7,因为它只表示在commit 6处master进入feature分支的同步事件; 但是,因为樱桃挑选/重新定位5和6 master会产生与合并相同的最终状态,所以不再需要合并提交.

    git rebase --onto master 7 feature
    
    # Squash 8 and 9 into S (which should be the tip of master).
    # You might have been able to do this in the above step too,
    # but I don't have time to double-check, so this will also work:
    git reset --soft master
    git commit --amend --no-edit
    
    # Once again, verify that the final result
    # matches that original of the feature branch:
    git diff backup
    
    Run Code Online (Sandbox Code Playgroud)

那应该是它(我认为).如果它不起作用,那么您可以随时重新设置功能分支

git reset --hard backup
git checkout master
git reset --hard 4
Run Code Online (Sandbox Code Playgroud)

解释为什么你之前做的事情没有按预期工作

好的,所以完整的解释会让我花一些时间来写.如果我以后有时间,我会回到这个答案并写出所有细节.但是,在我走之前,我只想指出:

  • Rebase 将提交重新应用为修补程序.当您从rebase中省略提交时,该提交中引入的更改将从所有后续的后代提交中丢失(它们不会保留在后代中).

与文件的必要联系