有没有一种非交互方式来压缩一些提交?

147

我正在尝试压缩一系列的提交 - 从 HEAD 到 HEAD~3。是否有快捷的方法可以做到这一点,还是需要使用交互式的 rebase 命令?


5
类似的问题:https://dev59.com/Vm435IYBdhLWcg3w1juV您需要将最后x个提交压缩在一起,使用git命令可以轻松完成。首先,使用"git rebase -i HEAD~x"命令打开交互式rebase界面,然后将"pick"关键字更改为"squash"或"s"以将它们压缩到前一个提交中。保存并关闭文件,继续进行rebase操作,将所有更改整合到一个提交中。最后,使用"git push --force"将更改推送到远程分支。 - koppor
8个回答

182

确保你的工作目录是干净的,然后

git reset --soft HEAD~3
git commit -m 'new commit message'

7
如果只是要减少提交次数:git reset --soft HEAD~3 && git commit -m "我的信息" - KingCrunch
9
@Phillip:您可以在Git别名中嵌入shell函数。 git config alias.mysquash'!f(){ git reset --soft HEAD〜$1 && git commit $ {2:+ -m“$2”};}; f'git mysquash 3'some message' 将起作用,但我还进行了调整,以便git musquash 3将完全省略 -m 标志,因此在这种情况下,您将获得交互式的git commit UI。 - Lily Ballard
这个问题导致了一个“![rejected] master -> master (non-fast-forward)”错误... - sebnukem
6
只是为了明确:这与挤压不同。挤压还将合并提交消息。如果您执行软重置,则会丢失所有提交的消息。如果要挤压,请尝试http://stackoverflow.com/a/27697274/974186。 - René Link
1
@sebnukem - 当我们尝试推送分支时,如果远程配置为拒绝强制推送,就会出现这种情况。 - avmohan
显示剩余5条评论

38

我个人喜欢Wilhelmtell提供的解决方案:

git reset --soft HEAD~3
git commit -m 'new commit message'

然而,我创建了一个带有错误检查的别名,这样你就可以这样做:

g.squash 3 'my commit message'

我建议设置别名,实际上运行脚本,这样更容易(a)编写你的脚本和(b)进行更复杂的错误检查。下面是一个执行压缩工作的脚本。我将其放在我的HOME路径下的scripts文件夹中。

压缩脚本(squash.sh)

#!/bin/bash
#

#get number of commits to squash
squashCount=$1

#get the commit message
shift
commitMsg=$@

#regular expression to verify that squash number is an integer
regex='^[0-9]+$'

echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"

echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
    echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
    echo "Invalid commit message.  Make sure string is not empty"
else
    echo "...input looks good"
    echo "...proceeding to squash"
    git reset --soft HEAD~$squashCount
    git commit -m "$commitMsg"
    echo "...done"
fi

echo
exit 0

然后将squash.sh脚本与别名关联起来,我在我的.zprofile文件中添加了以下内容:

注:保留原文中的html标签。

export PATH="$PATH:$HOME/scripts" # Add scripts folder to PATH
...
alias g.squash='function _gSquash(){ sh squash.sh $1 $2; };_gSquash'
...
注意:您可以将别名设置为任何您想要的名称。我将我的许多git快捷方式设置为g. <myCommand>

注意:您可以将别名设置为任何您想要的名称。我将我的许多git快捷方式设置为g.<myCommand>


4
您还可以将其放在 $PATH 中并将文件名命名为 git-squash.sh,那么它会自动别名为 git squash。我没有改变您的答案,以防使用 create-aliases.sh 脚本时有其他原因。 - user167661
1
我尝试了这个,它将我的提交信息读取为“压缩计数”,并失败了,因为它不是一个整数。 - Minthos
1
解决方案是在“/squash.sh $1 $2”后面添加“-”。 - Minthos
1
我喜欢这个解决方案的想法,但是解决方案中还没有考虑上面的评论。在单引号和双引号之间需要加一个减号。 - physicalattraction
如果之前的提交已经被推送了,我应该强制推送这个新创建的提交吗? - physicalattraction
显示剩余5条评论

28

wilhelmtell的回答基础上,我发现将软重置到HEAD~2并修改HEAD~3提交很方便:

git reset --soft HEAD~2
git commit --all --amend --no-edit    

这将会合并所有提交到HEAD~3提交中,并使用该提交的提交消息。请确保从一个干净的工作树开始。


12
这是正确的答案,因为它压缩了一系列提交,直到头部,并使用第一个提交的信息。它是非交互式的。 - Landon Kuhn
1
在这种情况下,我认为git commit中的--all参数是不必要的,因为软重置后修改的文件已经处于暂存状态。git commit --all文档 - li ki

11

我使用了:

EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>

工作得非常好。只是不要尝试在提交日志中以“pick”开头的行:


很遗憾,在我的Windows上这不起作用。仍然会得到交互式编辑器。 - abergmeier
在我的 Mac 上两者都不起作用。 - netimen

4

使用以下命令将最后4个提交压缩为一个提交:

git squash 4

使用别名:

squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf 

来自https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig


您没有指定在此处使用的操作系统/ shell。这个解决方案可能不适用于其他人使用的所有桌面。 - Rick-777
是的,你应该使用Linux/Bash|Zsh进行这个操作。 - brauliobo

2

以下是一行命令,用于合并最后两个提交。在此示例中,倒数第二个提交的消息将被保留。您可以根据需要更改消息。

git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"

如果您为此命令创建别名并使用别名代替该命令,则此命令将非常有用。


1
自从该分支从主分支派生以来,压缩所有内容:
git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author

--reedit-message=HEAD 将使用不包括压缩的最后一次提交的消息。这可能不是您想要的消息。要获取要包含的第一个提交的消息,可以采取以下两种方法之一:**(1)** 用您想要消息的提交哈希替换 HEAD;或者 (2) 跳到要包含的第一个提交并运行 git commit --amend --reedit-message=HEAD。这就是 harmonious 回答的方式。 - Maëlan

-1

你可以使用以下命令来实现:

git rebase --onto HEAD~4 HEAD~ master

这假设你在主分支上,且历史记录是线性的。它不完全是一个压缩操作,因为它会丢弃中间的提交记录。你需要修改新的 HEAD 来修改提交信息。


谢谢Greg;你所说的“discards”是指那些中间提交会被git gc清理吗? - Phillip
@Phillip 是的,中间提交记录以及旧的 HEAD 都变成了垃圾,因为它被重写为以 HEAD~4 作为其父提交记录。 - Greg Bacon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接