如何在git中压缩已推送的提交?

840
这里有一个很好的解释,可以帮你理解如何压缩多个提交记录:

http://git-scm.com/book/en/Git-Branching-Rebasing

但对于已经被推送的提交记录无效。我该如何压缩最近的几个提交记录,包括本地和远程仓库?当我运行 git rebase -i origin/master~4 master 命令时,在编辑器中将第一个设置为 pick,将其它三个设置为 squash,然后退出(在emacs中通过c-x c-c),我得到了:
$ git rebase -i origin/master~4 master
# Not currently on any branch.
nothing to commit (working directory clean)

Could not apply 2f40e2c... Revert "issue 4427: bpf device permission change option added"
$ git rebase -i origin/master~4 master
Interactive rebase already started

这里的2f40是pick提交。现在4个提交记录都没有出现在git log中。我原本期望编辑器会被重新启动以便我能输入提交信息。我做错了什么?


1
不是那么全面,但很容易阅读:https://www.internalpointers.com/post/squash-commits-into-one-git - Janos Vinceller
12个回答

1135

通过以下方式在本地压缩提交:

git rebase -i origin/master~4 master

在这里,~4 表示最后 4 个提交。

这将打开你的默认编辑器。在第二、第三和第四行中,将 pick 替换为 squash(因为你只关心最后 4 个提交)。第一行(对应最新的提交)应该保留为 pick。保存这个文件。

之后,你的编辑器会再次打开,显示每个提交的消息。注释掉你不感兴趣的提交(换句话说,保留与此合并操作相对应的提交消息的注释)。保存文件并关闭它。

然后,你需要再次使用 -f 标志进行推送。

然后使用以下命令进行强制推送:

git push origin +master

--force+之间的区别

根据git push的文档:

请注意,--force适用于所有被推送的引用,因此在将push.default设置为matching或使用多个配置了remote.*.push的推送目标时,可能会覆盖除当前分支以外的引用(包括严格落后于其远程对应分支的本地引用)。要强制只推送一个分支,请在要推送的refspec前面加上+(例如git push origin +master来强制推送到master分支)。


45
您可以使用 git push --force origin master 命令进行强制推送。 - Daenyth
9
是的,但我总是更喜欢这种语法,因为它更短。 - Alan Haggai Alavi
102
当然,要意识到如果其他人可能已经从远程仓库拉取,你可能不想这样做——在这种情况下的答案是“不要这样做”。 - Cascabel
10
另外,我认为 OP 正在完全复制命令 git rebase -i origin/master,实际上想知道如何将提交的内容向后移动,例如 git rebase -i origin/master~20 master - Cascabel
10
在更新的答案中,+ 只会强制推送被它前缀的 refspec,而 --force 则会强制推送所有的 refspec。请参考更新后的回答。 - Alan Haggai Alavi
显示剩余19条评论

300

在一个分支上,我能够像这样做(针对最近的4个提交)

git checkout my_branch
git reset --soft HEAD~4
git commit
git push --force origin my_branch

4
使用"soft"命令在已经推送的分支上执行此操作,结果将推送大量其他人的提交记录。 - cchamberlain
6
一吨?怎么会比4还多呢?你能详细说明一下吗? - jakob-r
15
我会接受这个预期答案。比接受的答案更加简洁。 - vikramvi
10
这是仅需4个步骤的最清晰、最被接受的答案。 - Ameya Salagre
1
@tuemar29 在日志中更改任何内容(例如使用强制推送)将在与他人合作时引起复杂情况。 - jakob-r
显示剩余7条评论

128

虽然与被接受的答案有一些细微的差异,但我一直在努力解决这个问题,最终终于解决了。

$ git rebase -i HEAD~4
  • 在打开的交互式屏幕上,将所有你想要压缩的提交中顶部的pick替换为squash
  • 保存并关闭编辑器。

使用以下命令将更改推送到远程:

$ git push origin branch-name --force

2
Brief and effective: 将以下与编程相关的内容从英语翻译成中文。仅返回翻译后的文本。Elaborate: 请将以下编程相关内容翻译为中文,确保翻译流畅有效。请注意,翻译应准确无误,并且不应该改变原始意思或添加任何个人解释。 - terwxqian
3
如果编辑器是vi或vim,那么"esc --> :wq"意思是按下"Esc"键后输入":wq"命令保存并退出。 - thanos.a
3
正确而精准。力推很重要,请不要在推之前拉。 - Jeff Manful
2
谢谢!提醒其他人一下,我使用了+branch-name而不是branch-name --force,正如另一个答案中建议的那样 - 这应该更安全。(https://dev59.com/_W035IYBdhLWcg3wBLTb#5668050) - Yoav Feuerstein

38

git rebase -i master

你将得到编辑器vm打开,内容类似于这样

Pick 2994283490 commit msg1
f 7994283490 commit msg2
f 4654283490 commit msg3
f 5694283490 commit msg4
#Some message 
#
#some more

我已将所有其他提交的pick更改为“f”(表示fixup)。

git push -f origin feature/feature-branch-name-xyz

这将把所有提交的fixup成一个提交,并删除所有其他提交。 我做了这个操作,它对我有所帮助。


1
这需要成为最佳答案,谢谢。我认为这个答案比仅基于提交数量盲目压缩更好。特别是在VM编辑器中,您可以同时执行许多操作,例如更改提交、删除提交和压缩。 - MG Developer

35

通过仅创建一个分支来工作并主分支上工作,可以避免许多问题:

git checkout -b mybranch

对于已推送的远程提交和一组混合的远程推送提交/仅本地提交,可以采用以下方法:

# example merging 4 commits

git checkout mybranch
git rebase -i mybranch~4 mybranch

# at the interactive screen
# choose fixup for commit: 2 / 3 / 4

git push -u origin +mybranch

我还有一些拉取请求笔记可能会有所帮助。


15

1) git rebase -i HEAD~4

解释:它在当前分支上运行;HEAD~4 表示将最近的四个提交压缩成一个;交互模式(-i)

2) 此时,编辑器打开,并显示要更改第二个及其后面的提交列表,将 pick 替换为 squash 然后保存。

输出:已成功地重新设置基准并更新了 refs/heads/branch-name。

3) git push origin refs/heads/branch-name --force

输出:

remote:
remote: To create a merge request for branch-name, visit:
remote: http://xxx/sc/server/merge_requests/new?merge_request%5Bsource_branch%5D=sss
remote:To ip:sc/server.git
 + 84b4b60...5045693 branch-name -> branch-name (forced update)

13

更新远程Sqush更改

  • 首先确保您的本地主分支与远程同步
  • 然后将本地feature分支重置为与主分支同步:git reset --soft master
  • 接着,将您在刚刚重置为主分支的本地feature分支上所做的所有修改和更改添加到暂存区:git add .
  • 然后提交这些更改:git commit -m "这是最终提交信息"
  • 最后强制推送到远程分支:git push RemoteBranch --force

7

想要将两个提交压缩成一个,其中一个已经被推送到单个分支上,可以采用以下方法:

git rebase -i HEAD~2
    [ pick     older-commit  ]
    [ squash   newest-commit ]
git push --force

默认情况下,这将把最新提交的提交信息作为注释添加到旧提交上。


7

当你在使用Gitlab或Github时,可能会遇到这种问题。你可以使用上述方法之一来压缩你的提交记录。我最喜欢的是:

git rebase -i HEAD~4
or
git rebase -i origin/master

选择用 "squash" 或 "fixup" 命令来提交您的更改。在这一点上,您可以使用 "git status" 命令进行确认。提交消息可能会是:

    On branch ABC-1916-remote
    Your branch and 'origin/ABC-1916' have diverged,
    and have 1 and 7 different commits each, respectively.
      (use "git pull" to merge the remote branch into yours)

你可能会想要拉它。 不要这样做,否则你将陷入与之前相同的情况。

相反,使用以下操作将其推回原始位置:

git push origin +ABC-1916-remote:ABC-1916

使用 + 可以强制推送到一个分支。


1
拉取代码没有问题,特别是如果有冲突,通过拉取可以更容易地解决,而不是强制推送。 - Ray_Poly
@Ray_Poly - 那不是会将那些远程提交作为本地提交吗?这就是我发现的。 - Steve Dunn
是的,它会创建一个新的提交,但您可以在该提交中查看差异,这比每次变基重写git历史要好。 - Ray_Poly

2

对于可能感兴趣的人,这是在GitHub PR中压缩提交记录的样子。

之前: 输入图像描述

之后:

输入图像描述


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