挤压Git中的前两个提交?

使用git rebase --interactive <commit> ,可以将任意数量的提交压缩成一个。

除非你想压缩到最初的提交,否则这很好。 这似乎不可能做到。

有什么办法可以实现吗?


中等相关:

在一个相关的问题上,我想出了一个不同的方法来解决第一个犯下这个问题的必要性,也就是把它作为第二个犯。

如果你感兴趣: git:如何插入一个提交作为第一,转移所有其他?

2012年7月更新( git 1.7.12+ )

你现在可以重新提交所有提交到根目录,并select第二个提交Y被第一个X压扁。

 git rebase -i --root master pick sha1 X squash sha1 Y pick sha1 Z 
 git rebase [-i] --root $tip 

这个命令现在可以用来重写从“ $tip ”到root提交的所有历史logging。

请参阅Chris Webb( arachsys )的 GitHub上的提交df5df20c1308f936ea542c86df1e9c6974168472 。


原答复(2009年2月)

我相信你会在SO问题中find不同的配方:“ 如何将git仓库的前两个提交结合起来?

Charles Bailey在那里提供了最详细的答案 ,提醒我们一个提交是一个完整的树(不仅仅是以前的状态)。
在这里,旧的提交(“初始提交”)和新的提交(压缩的结果)将没有共同的祖先。
这意味着你不能“ commit --amend ”初始提交到新的提交,然后重新绑定到新的初始提交前一个初始提交的历史(大量的冲突)

git rebase -i --root <aBranch>最后一句不再是真的)

相反( A是原始的“初始提交”, B是后续提交需要压缩到最初的提交):

  1. 返回到我们想要形成初始提交的最后一个提交(detach HEAD):

     git checkout <sha1_for_B> 
  2. 将分支指针重置为初始提交,但保持索引和工作树不变:

     git reset --soft <sha1_for_A> 
  3. 使用“B”中的树修改初始树:

     git commit --amend 
  4. 临时标记这个新的初始提交(或者您可以手动记住新的提交sha1):

     git tag tmp 
  5. 回到原来的分支(假设这个例子的主人):

     git checkout master 
  6. 将B之后的所有提交重放到新的初始提交中:

     git rebase --onto tmp <sha1_for_B> 
  7. 删除临时标记:

     git tag -d tmp 

这样,“ rebase --onto ”不会引起合并过程中的冲突,因为它将在上次提交( B之后创build的历史logging压缩到最初的一个(这是A )到tmp (代表压扁的新的初始提交):只有简单的快进合并。

这对于“ AB ”是有效的,而且也是“ A-...-...-...-B ”(任何数量的提交可以这样压缩到最初的提交)

我已经重写了VonC的脚本,自动完成所有的事情,不要问我任何事情。 你给它两个提交SHA1,它将压缩它们之间的一切到一个名为“压扁历史”的提交:

 #!/bin/sh # Go back to the last commit that we want # to form the initial commit (detach HEAD) git checkout $2 # reset the branch pointer to the initial commit (= $1), # but leaving the index and working tree intact. git reset --soft $1 # amend the initial tree using the tree from $2 git commit --amend -m "squashed history" # remember the new commit sha1 TARGET=`git rev-list HEAD --max-count=1` # go back to the original branch (assume master for this example) git checkout master # Replay all the commits after $2 onto the new initial commit git rebase --onto $TARGET $2 

对于它的价值,我通过总是创build一个“no-op”第一个提交来避免这个问题,在这个提交中,唯一的东西是一个空的.gitignore:

https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

这样,就没有任何理由搞砸了第一次提交。

这将压制第一个承诺:

ABC-... -> AB-C-...

 git filter-branch --commit-filter ' if [ "$GIT_COMMIT" = <sha1ofA> ]; then skip_commit "$@"; else git commit-tree "$@"; fi ' HEAD 

AB的提交信息将从B中提取(尽pipe我更喜欢A)。

与Uwe Kleine-König的答案具有相同的效果,但也适用于非初始A。

如果您只是想将所有提交压缩到一个初始提交中,只需重置该存储库并修改第一个提交:

 git reset hash-of-first-commit git add -A git commit --amend 

Git重置会使工作树完好无损,所以一切仍然存在。 所以只需使用git add命令添加文件,并修改第一次提交这些更改。 与rebase -i相比,您将失去合并git注释的能力。

压缩第一次和第二次提交会导致第一次提交被重写。 如果你有多个基于第一次提交的分支,那么你会切断那个分支。

考虑下面的例子:

 a---b---HEAD \ \ '---d 

将a和b压缩成一个新的提交“ab”会导致两个不同的树,在大多数情况下这是不可取的,因为git-mergegit-rebase将不再在两个分支上工作。

 ab---HEAD a---d 

如果你真的想要这个,就可以做到。 看看git-filter-branch为一个强大的(和危险的)历史重写工具。

你可以使用git filter-branch。 例如

 git filter-branch --parent-filter \ 'if test $GIT_COMMIT != <sha1ofB>; then cat; fi' 

这导致AB-C丢掉A的提交日志。

在推送到远程之前,可以使用rebase interactive来修改最后两个提交

 git rebase HEAD^^ -i 

有一个更简单的方法来做到这一点。 假设你在master分支上

创build一个新的孤立分支,它将删除所有提交历史logging:

 $ git checkout --orphan new_branch 

添加你的初始提交信息:

 $ git commit -a 

摆脱旧的没有主分支:

 $ git branch -D master 

重命名你的当前分支new_branchmaster

 $ git branch -m master