使用git rebase --interactive <commit>
命令,您可以将任意数量的提交合并成一个单独的提交。
很好,但是如果想将提交压缩到初始提交中,则似乎不可能进行。
有什么方法可以实现吗?
相关问题:
在一个相关的问题中,我设法提出了另一种针对需要对第一个提交进行压缩的方法,那就是将其变为第二个提交。
使用git rebase --interactive <commit>
命令,您可以将任意数量的提交合并成一个单独的提交。
很好,但是如果想将提交压缩到初始提交中,则似乎不可能进行。
有什么方法可以实现吗?
在一个相关的问题中,我设法提出了另一种针对需要对第一个提交进行压缩的方法,那就是将其变为第二个提交。
2012年7月更新 (git 1.7.12+)
现在,您可以将所有提交的内容变基到根,并选择第二次提交Y
与第一次提交X
合并。
git rebase -i --root master
pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip
This command can now be used to rewrite all the history leading from "
$tip
" down to the root commit.
查看来自Chris Webb (arachsys
)的commit df5df20c13 (rebase -i:支持不带--onto的--root, 2012-06-26)在GitHub上。
正如评论中所述,在任何rebase
操作后,如果您需要在远程存储库中发布该重制版,则需要使用git push --force-with-lease
(比--force
更安全,因为Mikko Mantalainen 提醒我们)。
原始答案(2009年2月)
我相信你会在SO问题“如何将Git存储库的前两个提交组合在一起?”中找到不同的解决方案。
Charles Bailey在那里提供了最详细的答案,提醒我们提交是完整的树(不仅是与先前状态的差异)。
在这里,旧的提交("初始提交")和新的提交(压缩的结果)将没有共同的祖先。
这意味着你不能将初始提交"commit --amend
"成为新的提交,然后将以前初始提交的历史记录(大量冲突)重新基于新的初始提交。
(使用git rebase -i --root <aBranch>
就不再是事实了)
相反(使用A
原始的"初始提交",B
是需要压缩到初始提交的后续提交):
Go back to the last commit that we want to form the initial commit (detach HEAD):
git checkout <sha1_for_B>
Reset the branch pointer to the initial commit, but leaving the index and working tree intact:
git reset --soft <sha1_for_A>
Amend the initial tree using the tree from 'B':
git commit --amend
Temporarily tag this new initial commit (or you could remember the new commit sha1 manually):
git tag tmp
Go back to the original branch (assume master for this example):
git checkout master
Replay all the commits after B onto the new initial commit:
git rebase --onto tmp <sha1_for_B>
Remove the temporary tag:
git tag -d tmp
rebase --onto
"在合并过程中不会引入冲突,因为它将历史记录(B
之后的记录)变基到tmp
(表示压缩后的新初始提交),以便与最初的提交(A
)进行平凡快进合并。这适用于"A-B
",也适用于"A-...-...-...-B
"(任意数量的提交可以通过这种方式压缩成一个初始提交)。push --force
;) 请参阅 https://dev59.com/jmUp5IYBdhLWcg3wz58H。@MattHuggins - VonC--force
和--force-with-lease
之间差异的链接。 - VonC如果您只想将所有提交合并为一个初始提交,只需重置存储库并修正第一个提交:
git reset hash-of-first-commit
git add -A
git commit --amend
Git reset命令会保留工作树,因此所有文件都还在。只需使用git add命令添加文件,并使用这些更改修改第一个提交。与交互式变基(rebase -i)相比,您将失去合并git注释的能力。
我重新修改了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
就我个人而言,为避免这个问题,我总是先创建一个"空操作(no-op)"的第一次提交,仓库中唯一的东西就是一个空的.gitignore文件:
https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit
这样,就不会有必要去修改第一次提交了。
git commit --allow-empty -m empty
经常是我的第一个提交。这甚至避免了在提交中添加 .gitignore
文件的“污染”。请注意,一些旧工具可能无法查看这样的空树。 - TinoA-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也有效。
git rev-list --reverse HEAD | head -n1
可能是答案。 - Antony Hatchkinsa---b---HEAD
\
\
'---d
ab---HEAD
a---d
git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'
在提交到远程之前,您可以使用交互式变基来修改最后两个提交
git rebase HEAD^^ -i
有一个更简单的方法来实现这个。假设你在master
分支上:
创建一个新的孤立分支,它会删除所有提交历史记录:
$ git checkout --orphan new_branch
添加您的初始提交信息:
$ git commit -a
摆脱旧的未合并主分支:
$ git branch -D master
将您当前的分支new_branch
重命名为master
:
$ git branch -m master
squash =!“f(){NL = $ 1; GIT_EDITOR = \”sed -i'2,$ NL s / pick / squash /; /#这是第二个提交消息:/,$ {d}'\“; git rebase -i HEAD〜$ NL;}; f”
进行一行命令git squash 2
。请参阅 http://stackoverflow.com/a/28789349/670229。 - brauliobo