6

我的 git 树中有以下情况:

1 -- 2 -- 3 -- 4 <-- master
      \         \
       5 -- 6 -- 7 -- 8 -- 9 <-- feature

我想 rebase 和 squash 功能中的所有内容,以便我可以通过一次提交添加的功能来推进 master。

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

git rebase -i -p master

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

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

1 -- 2 -- 3 -- 4 <-- master
                \
                 5 -- 6 -- 789 <-- feature

5 和 6 在同一个分支中的存在让我感到困惑,但同样,由于它们已经包含在 7 中(现在在 789 中),我可以丢弃它们。

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

但是,这里和那里发生了冲突,所以我放弃了整个事情。

我目前处于那个阶段,但我的远程分支尚未更新,所以我不妨重置为原始状态。

哪些是正确的步骤,可以让我到达我想要的位置,而无需手动解决所有合并冲突?

4

2 回答 2

6

我想 rebase 和 squash 功能中的所有内容,以便我可以通过一次提交添加的功能来推进 master。

这正是--squashoption for 的git merge作用。来自git merge 的文档

- 壁球

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

(强调我的)

使用示例:

git checkout master
git merge feature --squash
git commit
于 2014-07-31T20:58:35.347 回答
5

替代 Ajedi32 的解决方案

Ajedi32 的解决方案绝对是迄今为止最好和最简单的解决方案,但为了完整起见,我只是想展示一个几乎以相同方式工作的替代方案。--squash选项git merge基本上复制了您要合并的分支的最终提交的整个状态,因为每个提交都代表一个完整的快照1

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

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

1另一种理解方式是将其视为从基础分支的尖端到要合并的分支的尖端的所有差异的总和。

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

好的,有两件事:

  1. 我有一个适合你的解决方案
  2. 为什么你目前正在做的事情不起作用的解释可能是错误的。

一个解法?(这真的有效吗?)

我们将部分地重新设置您的功能分支。当这些合并提交的父级不包括来自您试图在其之上重新设置的其他分支的提交时,--preserve-merges标志的效果最好,我相信您已经看到了。git rebase

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

  1. 首先,做一个备份分支,以防万一出现可怕的错误,我们最终需要硬重置回你的特性分支的原始状态:

    git branch backup feature
    
  2. 接下来,我们将压缩 5 和 6,然后将它们挑选出来master(你也可以将它们变基master,但由于我们只是挑选一个提交,因此变基有点过分了):

    git checkout -b temp 6
    git reset --soft HEAD^
    git commit -m "Enter your squash commit message here"
    

    现在我们已经将 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
    
  3. 接下来,我们将变基 8 和 9。我们跳过 7,因为它只是表示在提交 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
    

应该是这样(我认为)。如果它不起作用,那么您可以随时硬重置您的功能分支

git reset --hard backup
git checkout master
git reset --hard 4

解释为什么您之前所做的没有按预期工作

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

  • Rebase将提交重新应用为补丁。当您从变基中遗漏提交时,该提交中引入的更改会从所有后续的后代提交中丢失(它们不会保留在后代中)。

文档的强制性链接

于 2014-07-31T19:52:32.420 回答