题 使用Git将我的最后一次X提交压缩在一起


如何使用Git将我最后的X提交压缩成一个提交?


2322
2018-03-04 04:11


起源


类似的问题: stackoverflow.com/questions/7275508/... - koppor
有关: Git - 在推送之前组合多个提交。
@matt TortoiseGit是你的工具。它提供单个功能“组合到一个提交”,它将在后台自动调用所有步骤。不幸的是只适用于Windows。请参阅下面的答案。 - Matthias M
@Thomas:请解释在必须对已提交的代码执行评论时如何“使用git”,我们需要根据评论评论进行更新并再次提交,但又不想推动基于评论的流失? - Jeff Learman
为了挤压第一次提交,请看 - stackoverflow.com/questions/1657017/... - goelakash


答案:


使用 git rebase -i <after-this-commit> 并使用“squash”或“fixup”替换第二次和后续提交中的“pick”,如中所述 手册

在这个例子中, <after-this-commit> 是SHA1哈希值或当前分支的HEAD的相对位置,从中为rebase命令分析提交。例如,如果用户希望在过去查看当前HEAD的5次提交,则命令为 git rebase -i HEAD~5


1268
2018-03-04 04:18



我认为,这可以更好地回答这个问题 stackoverflow.com/a/5201642/295797 - Roy Truelove
是什么意思 <after-this-commit>? - jtheletter
<after-this-commit> 是提交X + 1,即您要压缩的最早提交的父级。 - joozek
我发现这个回复太简洁了,无法明确理解。一个例子会有所帮助。 - Ian Ollmann
这个区别 rebase -i 方法和 reset --soft 是, rebase -i允许我保留提交作者,同时 reset --soft 允许我重新承诺。有时我需要压缩提交请求,同时维护作者信息。有时候我需要在自己的提交中重置软件。无论如何,赞成这两个伟大的答案。 - zionyx


你可以相当容易地做到这一点 git rebase 要么 git merge --squash。在这个例子中,我们将压缩最后3次提交。

如果要从头开始编写新的提交消息,这就足够了:

git reset --soft HEAD~3 &&
git commit

如果你想用现有提交消息的串联开始编辑新的提交消息(即类似于pick / squash / squash / ... / squash) git rebase -i 指令列表会启动你,然后你需要提取这些消息并将它们传递给 git commit

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

这两种方法都以相同的方式将最后三次提交压缩成一个新的提交。软复位只是将HEAD重新指向您不想压缩的最后一次提交。软复位不会触及索引和工作树,使索引处于新提交所需的状态(即,它已经具有您即将“扔掉”的提交的所有更改)。


2603
2018-03-05 04:19



哈!我喜欢这种方法。这是一个接近问题精神的人。遗憾的是它需要这么多伏都教。应该将这样的东西添加到其中一个基本命令中。或者 git rebase --squash-recent, 甚至 git commit --amend-many。 - Adrian Ratnapala
@ A-B-B:如果您的分支有“上游”设置,那么您可以使用 branch@{upstream} (要不就 @{upstream} 对于当前的分支;在这两种情况下,最后一部分都可以缩写为 @{u};看到 gitrevisions)。这可能不同于 你的 “最后推送提交”(例如,如果其他人推动了在你最近的推动之上构建的东西然后你拿到了​​它),但似乎它可能接近你想要的。 - Chris Johnsen
这种类型要求我 push -f 但除此之外很可爱,谢谢。 - 2rs2ts
@ 2rs2ts git push -f sound危险。注意只能压制本地提交。永远不要触摸推送提交! - Matthias M
感谢您提供的解决方案不需要为每次提交手动键入squash / fixup,这与其他地方的所有交互式rebase建议不同。 - Antimony


您可以使用 git merge --squash 为此,这比稍微优雅一点 git rebase -i。假设您是主人,并且您希望将最后12次提交压缩为一次。

警告:首先确保您提交工作检查 git status 很干净(因为 git reset --hard 将抛弃分阶段和未分阶段的变化)

然后:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

的文件 git merge 描述了 --squash 选项更详细。


更新: 这种方法唯一真正的优点就是更简单 git reset --soft HEAD~12 && git commit 克里斯约翰森在 他的回答 是你获得提交消息预填充每个提交消息,你正在挤压。


575
2018-03-04 06:10



你说这比'优雅'更优雅 git rebase -i,但你没有给出理由。试探性地说明这一点,因为在我看来事实上情况正好相反,这是一个黑客攻击;你是不是为了强迫而执行的命令多于必要的命令 git merge 做其中的一件事 git rebase 是专门为? - Mark Amery
@Mark Amery:有很多原因我说这更优雅。例如,它不涉及不必要地生成编辑器,然后在“待办事项”文件中搜索和替换字符串。运用 git merge --squash 在脚本中也更容易使用。从本质上讲,理由是你不需要“互动性” git rebase -i 完全是为了这个。 - Mark Longair
另一个优点是 git merge --squash 与变基相比,面对移动/删除/重命名时产生合并冲突的可能性较小,尤其是当您从本地分支合并时。 (免责声明:根据一种经验,如果在一般情况下不是这样,请纠正我!) - Cheezmeister
在硬复位时我总是非常不情愿 - 我使用的是时态标签而不是 HEAD@{1} 只是为了安全起见,例如当您的工作流程因断电而中断一小时 - Tobias Kienzler
@B T:破坏你的提交? :(我不确定你的意思。你提交的任何东西都可以轻松地从git的reflog中恢复。如果你有未提交的工作,但文件已经上演,你仍然可以得到他们的内容回来了 虽然那会更多的工作。然而,如果你的工作没有上演,我恐怕没有什么可以做的;这就是为什么答案在前面说:“首先检查git状态是否干净(因为git reset --hard将丢弃分阶段和未分阶段的更改)”。 - Mark Longair


我建议避免 git reset 在可能的情况下 - 特别是对于Git-novices。除非你真的需要基于a自动化流程  提交,有一种不那么奇特的方式......

  1. 将待压缩的提交放在工作分支上(如果它们还没有) - 请使用gitk
  2. 检查目标分支(例如'master')
  3. git merge --squash (working branch name)
  4. git commit

提交消息将根据壁球预先填充。


136
2018-03-14 23:24



这是最安全的方法:没有重置软/硬(!!),或使用reflog! - TeChn4K
如果你扩展(1)会很棒。 - Adam
@Adam:基本上,这意味着使用GUI界面 gitk 标记您正在压缩的代码行,并标记要压缩的基础。在正常情况下,这两个标签都已存在,因此可以跳过步骤(1)。 - nobar
请注意,此方法不会将工作分支标记为完全合并,因此删除它需要强制删除。 :( - Kyrstellaine
对于(1),我发现了 git branch your-feature && git reset --hard HEAD~N 最方便的方式。但是,它确实再次涉及git重置,这个答案试图避免。 - eis


基于 克里斯约翰森的回答

从bash添加全局“squash”别名:(或Windows上的Git Bash)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

...或使用Windows的命令提示符:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


你的 ~/.gitconfig 现在应该包含这个别名:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


用法:

git squash N

...它自动挤压最后一个 N 承诺,包容性。

注意:结果提交消息是所有压缩提交的组合,按顺序。如果你对此不满意,你可以随时 git commit --amend手动修改它。 (或者,编辑别名以符合您的口味。)


97
2018-02-19 19:21



有意思,但我更愿意自己输入压缩的提交消息,作为我的多次提交的描述性摘要,而不是为我自动输入。所以我宁愿指出 git squash -m "New summary." 并有 N 自动确定为未提交的提交数。 - A-B-B
@ A-B-B,这听起来像是一个单独的问题。 (我不认为这正是OP所要求的;我在git squash工作流程中从未感到需要它。) - EthanB
这很可爱。就个人而言,我想要一个使用第一个压缩提交提交消息的版本。对于像空白调整这样的事情会有好处。 - funroll
@funroll同意。只丢弃最后一次提交消息对我来说是一个非常普遍的需求。我们应该能够设计...... - Steve Clay
@ A-B-B你可以使用 git commit --amend 进一步更改消息,但是这个别名可以让你在提交消息中有一个良好的开端。 - dashesy


如果您使用TortoiseGit,您可以使用该功能 Combine to one commit

  1. 打开TortoiseGit上下文菜单
  2. 选择 Show Log
  3. 在日志视图中标记相关提交
  4. 选择 Combine to one commit 从上下文菜单中

Combine commits

此函数自动执行所有必需的单个git步骤。 不幸的是只适用于Windows。


46
2017-11-06 12:51



据我所知,这对合并提交不起作用。 - Thorkil Holm-Jacobsen
虽然它没有被任何其他人评论,但这甚至适用于不在HEAD的提交。例如,我需要在推送之前用一个更理智的描述压缩我做的一些WIP提交。工作得很漂亮。当然,我仍然希望我能通过命令学习如何做到这一点。 - Charles Roberto Canato


谢谢 这个方便的博客文章 我发现你可以使用这个命令来压缩最后3次提交:

git rebase -i HEAD~3

即使您在没有跟踪信息/远程仓库的本地分支上,它也很方便。

该命令将打开交互式rebase编辑器,然后允许您按照正常情况重新排序,压缩,重写等。


46
2018-05-17 06:19