在Git中压缩前两个提交?

时间:2009-02-28 20:55:09

标签: git rebase git-rebase squash

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

除非你想将提交压缩到初始提交中,否则这一切都很棒。这似乎无法做到。

有没有办法实现它?


中度相关:

在一个相关的问题中,我设法提出了一种不同的方法来解决第一次提交的问题,这是第二次提交。

如果您有兴趣:git: how to insert a commit as the first, shifting all the others?

9 个答案:

答案 0 :(得分:654)

2012年7月更新(git 1.7.12+

您现在可以将所有提交重新绑定到root,并选择第二个提交Y以使用第一个X压缩。

git rebase -i --root master

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

此命令现在可用于重写从“$tip”到根提交的所有历史记录。

请参阅commit df5df20c1308f936ea542c86df1e9c6974168472 on GitHub中的Chris Webb (arachsys)


原始答案(2009年2月)

我相信你会在SO问题“ How do I combine the first two commits of a git repository? ”中找到不同的食谱

Charles Bailey提供了最多detailed answer,提醒我们提交是一个完整的树(不仅仅是以前状态的差异)。
在这里,旧的提交(“初始提交”)和新的提交(挤压的结果)将没有共同的祖先。
这意味着您不能“commit --amend”初始提交到新的提交,然后将新的初始提交重新绑定到上一次初始提交的历史记录(许多冲突)

(最后一句话不再适用于git rebase -i --root <aBranch>

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

  1. 回到我们想要形成初始提交的最后一次提交(分离HEAD):

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

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

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

    git tag tmp
    
  5. 返回原始分支(假设此示例为master):

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

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

    git tag -d tmp
    
  8. 这样,“rebase --onto”在合并期间不会引入冲突,因为它会将最后一次提交之后创建的历史记录(B)重新压缩到初始值(A)到tmp(表示压缩的新初始提交):仅仅是简单的快进合并。

    适用于“A-B”,但也适用于“A-...-...-...-B”(任何数量的提交都可以通过这种方式压缩到最初的提交中)

答案 1 :(得分:30)

我重写了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

答案 2 :(得分:23)

对于它的价值,我通过始终创建一个“no-op”第一次提交来避免这个问题,其中存储库中唯一的东西是空的.gitignore:

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

这样,从来没有任何理由搞乱第一次提交。

答案 3 :(得分:16)

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

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

Git reset会使工作树保持原样,所以一切都还在那里。所以只需使用git add命令添加文件,并使用这些更改修改第一次提交。与rebase -i相比,你将失去合并git注释的能力。

答案 4 :(得分:5)

这会将第二次提交压缩到第一次提交:

A-B-C-... -> AB-C-...

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

AB的提交信息将从B中获取(虽然我更喜欢A)。

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

答案 5 :(得分:3)

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

考虑以下示例:

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

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

ab---HEAD

a---d

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

答案 6 :(得分:3)

你可以使用git filter-branch。 e.g。

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

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

答案 7 :(得分:1)

您可以使用rebase interactive来修改最后两次提交,然后再将它们推送到远程

git rebase HEAD^^ -i

答案 8 :(得分:-5)

有一种更简单的方法可以做到这一点。我们假设你在master分支

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

$ git checkout --orphan new_branch

添加您的初始提交消息:

$ git commit -a

摆脱旧的未合并主分支:

$ git branch -D master

将当前分支new_branch重命名为master

$ git branch -m master