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

时间:2011-03-04 04:11:27

标签: git squash git-squash

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

40 个答案:

答案 0 :(得分:3176)

如果没有git rebasegit 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重新指向您不想压缩的最后一次提交。软复位不会触及索引和工作树,而是将索引保留在新提交的所需状态中(即,它已经具有您将要“扔掉”的提交的所有更改)。

答案 1 :(得分:1661)

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

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

答案 2 :(得分:655)

您可以使用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

documentation for git merge更详细地介绍了--squash选项。


更新:此方法相对于Chris Johnsen在his answer中建议的更简单git reset --soft HEAD~12 && git commit的唯一真正优势是,您可以获得提交消息,其中包含每个提交消息的预先填充你正在挤压。

答案 3 :(得分:182)

我建议尽可能避免使用git reset - 特别是对于Git-novices。除非你真的需要根据提交的自动化流程,否则有一种不那么奇特的方式......

  1. 将待压缩的提交放在工作分支上(如果它们尚未提交) - 使用gitk进行此操作
  2. 查看目标分支(例如'master')
  3. git merge --squash (working branch name)
  4. git commit
  5. 提交消息将根据壁球预先填充。

答案 4 :(得分:117)

基于Chris Johnsen's answer

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

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手动修改它。 (或者,编辑别名以符合您的口味。)

答案 5 :(得分:89)

感谢this handy blog post我发现您可以使用此命令来压缩最后3次提交:

git rebase -i HEAD~3

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

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

使用交互式rebase编辑器:

交互式rebase编辑器显示最后三次提交。运行命令HEAD~3时,此约束由git rebase -i HEAD~3确定。

最新的提交HEAD首先显示在第1行。以#开头的行是注释/文档。

显示的文档非常清楚。在任何给定的行上,您可以将命令从pick更改为您选择的命令。

我更喜欢使用命令fixup作为这个&#34; squashes&#34;提交更改为上面一行的提交并丢弃提交的消息。

由于第1行的提交为HEAD,因此在大多数情况下,您将其保留为pick。  您不能使用squashfixup,因为没有其他提交来压缩提交。

interactive rebase editor

答案 6 :(得分:73)

2020 Simple 解决方案,无需重新设置基准:

git reset --soft HEAD~2

git commit -m "new commit message"

git push -f

2表示将压缩最后两个提交。您可以用任何数字替换它

答案 7 :(得分:53)

如果你使用TortoiseGit,你可以使用函数Combine to one commit

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

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

答案 8 :(得分:40)

基于this article我发现这个方法对我的用例更容易。

我的'dev'分支超过'origin / dev'提交了96次(所以这些提交还没有被推送到远程)。

我想在推动改变之前将这些提交压缩成一个。我倾向于将分支重置为'origin / dev'状态(这将保留96次提交中的所有更改未分级),然后立即提交更改:

git reset origin/dev
git add --all
git commit -m 'my commit message'

答案 9 :(得分:37)

为此,您可以使用以下git命令。

 git rebase -i HEAD~n

n(此处为= 4)是最后一次提交的次数。然后你有以下选项,

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

像bellow一样更新,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

有关详情,请点击Link

祝你好运!!

答案 10 :(得分:29)

1)识别提交短哈希

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

这里甚至可以使用git log --oneline来获得短哈希。

2)如果你想压缩(合并)最后两次提交

# git rebase -i deab3412 

3)这将打开一个nano编辑器进行合并。它看起来像下面

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4)将pick重命名为squash之前的abcd1234。重命名后,它应该如下所示。

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5)现在保存并关闭nano编辑器。按ctrl + o,然后按Enter进行保存。然后按ctrl + x退出编辑器。

6)然后再次打开nano编辑器以更新注释,如有必要,请更新注释。

7)现在它被成功压扁,您可以通过检查日志来验证它。

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8)现在推动回购。请注意在分支名称前添加+符号。这意味着强制推动。

# git push origin +master

注意:这是基于在ubuntu shell上使用git。如果您使用不同的操作系统(WindowsMac),那么除编辑器外,上述命令是相同的。你可能会得到不同的编辑。

答案 11 :(得分:27)

Anomies answer很好,但是对此我感到不安全,因此我决定添加一些屏幕截图。

步骤0:git log

使用git log查看您的位置。最重要的是,找到您不想想要压扁的第一个提交的提交哈希。所以只有:

enter image description here

步骤1:git rebase

在我的情况下,执行git rebase -i [your hash]

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

步骤2:选择/压榨想要的东西

就我而言,我想压缩第一次提交中的所有内容。顺序是从头到尾,与git log中的顺序完全相反。就我而言,我想要:

enter image description here

第3步:调整消息

如果您只选择了一个提交,而其余的都被压缩,则可以调整一个提交消息:

enter image description here

就是这样。保存此(:wq)后,就完成了。用git log来看看。

答案 12 :(得分:25)

如果您位于从Golden Repository(feature-branch)克隆的远程分支(称为golden_repo_name)上,那么这是将您的提交压缩为一个的技术:

  1. 查看黄金回购

    git checkout golden_repo_name
    
  2. 从中创建一个新分支(黄金回购),如下所示

    git checkout -b dev-branch
    
  3. 壁球与您已经

    的本地分支合并
    git merge --squash feature-branch
    
  4. 提交您的更改(这将是dev-branch中唯一的提交)

    git commit -m "My feature complete"
    
  5. 将分支推送到本地存储库

    git push origin dev-branch
    

答案 13 :(得分:23)

这是执行后的另一个可视化示例: git rebase -i HEAD~3

Here there is a visual example of what would follow after executing git rebase for the three last commits

来源:https://www.git-tower.com/learn/git/faq/git-squash/

答案 14 :(得分:22)

要在分支上合并提交,请运行:

git rebase -i HEAD~(n number of commits back to review)

示例:

git rebase -i HEAD~1

这将打开文本编辑器,如果您希望将这些提交合并在一起,则必须在每个提交前面将“ pick”切换为“ squash”。来自文档:

p,选择=使用提交

s,squash =使用提交,但可以合并到以前的提交中

例如,如果您希望将所有提交合并为一个,则“ pick”是您进行的第一个提交,所有以后的提交(置于第一个之后)应设置为“ squash”。如果使用vim,请在插入模式下使用:x 保存并退出编辑器。

然后继续进行重新设置:

git rebase --continue

有关此方法以及重写提交历史记录的其他方法的更多信息,请参见this helpful post

答案 15 :(得分:16)

这是超级愚蠢的克鲁迪,但是以一种很酷的方式,所以我只是把它扔进戒指:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

翻译:为git提供一个新的“编辑器”,如果要编辑的文件名为git-rebase-todo(交互式rebase提示符),则将除第一个“pick”之外的所有内容更改为“squash”,否则会生成vim - 这样当你被提示编辑压缩的提交消息时,你会得到vim。 (显然我在分支foo上压缩了最后五次提交,但是你可以根据自己的意愿改变它。)

我可能会做Mark Longair suggested {。}}。

答案 16 :(得分:15)

真正方便的是:
找到你要压缩的提交哈希,比如d43e15

现在使用

git reset d43e15
git commit -am 'new commit name'

答案 17 :(得分:14)

如果您想将每个提交压缩成一次提交(例如,第一次公开发布项目时),请尝试:

git checkout --orphan <new-branch>
git commit

答案 18 :(得分:10)

我认为最简单的方法是通过从master创建一个新分支并进行合并 - 对功能分支进行处理。

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

然后,您已准备好提交所有更改。

答案 19 :(得分:8)

要将最后10次提交压缩为1次单次提交:

git reset --soft HEAD~10 && git commit -m "squashed commit"

如果您还想使用压缩的提交来更新远程分支:

git push -f

答案 20 :(得分:7)

⚠️警告:“我最近的X次提交”可能是模棱两可的。

int main() {
    std::fstream fin("detect_char.txt");
    char x;
    while (fin >> x) {
        if (!isdigit(x)) {
            std::cout << "found non-int value = " << x << '\n';
        }
    }
    std::cout << '\n';
    return 0;
}

https://github.com/fleetwood-mac/band-history存储库的这段简短的历史中,您已经打开了一个合并请求,以将Bill Clinton提交合并到原始的{ (MASTER) Fleetwood Mac Fritz ║ ║ Add Danny Lindsey Stevie Kirwan Buckingham Nicks ║ ╚═══╦══════╝ Add Christine ║ Perfect Buckingham ║ Nicks LA1974══════════╝ ║ ║ Bill <══════ YOU ARE EDITING HERE Clinton (CHECKED OUT, CURRENT WORKING DIRECTORY) )Fleetwood Mac提交中。

您打开了一个拉取请求,并在GitHub上看到了:

四次提交:

  • 添加Danny Kirwan
  • 添加Christine Perfect
  • LA1974
  • 比尔·克林顿

认为没有人会愿意阅读完整的存储库历史记录。 (实际上有一个存储库,单击上面的链接!)您决定压缩这些提交。因此,您可以运行MASTER。然后将其git reset --soft HEAD~4 && git commit移至GitHub上以清理您的PR。

会发生什么?您刚刚完成了一次从Fritz到Bill Clinton的提交。因为您忘记了昨天您正在从事该项目的白金汉尼克斯版本。而且git push --force与您在GitHub上看到的不匹配。

故事道德

  1. 找到所需的确切文件,然后git log
  2. 找到要保留在历史记录中的确切先前提交,然后git checkout
  3. 制作一个git reset --soft,使其直接从起点弯曲到终点

答案 21 :(得分:7)

简单的单行代码始终有效,因为您当前在要压扁的分支上,master是它源自的分支,并且最新的commit包含您希望使用的提交消息和作者:

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}

答案 22 :(得分:5)

与这样的工作流程有关的问题的答案如何?

  1. 许多本地提交,与来自主站点的多个合并混合,
  2. 最终将其推送到远程
  3. PR并合并审阅人的主母。 (是的,开发人员在PR后更容易merge --squash,但团队认为这样做会减慢该过程。)

在此页面上没有看到这样的工作流程。 (可能是我的眼睛。)如果我正确理解rebase,则多次合并将需要多种冲突解决方案。我什至不想考虑!

所以,这似乎对我们有用。

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. 在本地进行很多编辑和提交,定期合并master
  5. git checkout new-branch
  6. git merge --squash new-branch-temp //将所有更改置于阶段
  7. git commit 'one message to rule them all'
  8. git push
  9. 审阅者执行PR并合并到母版。

答案 23 :(得分:4)

git rebase -i HEAD^^

其中^的数量是X

(在这种情况下,压缩最后两次提交)

答案 24 :(得分:4)

质疑“最后”的意思可能含糊不清。

例如git log --graph输出以下(简化):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

然后最后提交的时间是H0,合并,B0。要压缩它们,你必须在提交H1上重新绑定你的合并分支。

问题是H0包含H1和H2(通常在合并之前和分支之后提交更多),而B0则不包含。因此,您必须至少管理来自H0,合并,H1,H2,B0的更改。

可以使用rebase但以不同的方式使用其他提到的答案:

rebase -i HEAD~2

这将显示您的选择选项(如其他答案中所述):

pick B1
pick B0
pick H0

将壁球放入H0:

pick B1
pick B0
s H0

保存并退出后,rebase将在H1之后依次应用提交。这意味着它会要求您再次解决冲突(首先HEAD将是H1,然后在应用时累积提交)。

在rebase完成后,您可以选择压缩H0和B0的消息:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

P.S。如果你只是重置BO: (例如,使用此处https://stackoverflow.com/a/18690845/2405850更详细说明的reset --mixed):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

然后你压缩到H0,H1,H2的B0变化(在分支之后和合并之前完全没有变更提交。

答案 25 :(得分:4)

我发现更通用的解决方案是不指定N&#39; N&#39;提交,而不是你想要压缩的分支/ commit-id。这比计算提交直到特定提交更不容易出错 - 只需直接指定标记,或者如果你真的想要计数,你可以指定HEAD~N。

在我的工作流程中,我开始了一个分支,我在该分支上的第一次提交总结了目标(即,通常我将推送的内容作为该功能的最终&#39;消息公共存储库。)所以当我完成后,我想要做的就是git squash master回到第一条消息,然后我就可以推送了。

我使用别名:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

这将在它被删除之前转储被压缩的历史记录 - 如果你想恢复,这会让你有机会通过从控制台获取旧的提交ID来恢复。 (Solaris用户注意到它使用GNU sed -i选项,Mac和Linux用户应该没问题。)

答案 26 :(得分:3)

除了其他优秀的答案之外,我还想补充git rebase -i总是让我对提交顺序感到困惑的事情 - 旧版本更新版本更新,反之亦然?这是我的工作流程:

  1. git rebase -i HEAD~[N],其中N是我想要加入的提交数,从最近的开始。所以git rebase -i HEAD~5意味着&#34;将最后5次提交压缩成新的#34 ;;
  2. 弹出编辑器,显示我要合并的提交列表。现在它们以逆序显示:旧提交位于顶部。标记为&#34;壁球&#34;或&#34; s&#34; 除了第一个/更旧的之外的所有提交:它将用作起点。保存并关闭编辑器;
  3. 编辑器再次弹出新提交的默认消息:将其更改为您的需要,保存并关闭。壁球完成了!
  4. 来源&amp;其他内容如下:#1#2

答案 27 :(得分:3)

简单的解决方案:

git reset --soft HEAD~5

git commit -m "commit message"

git push origin branch --force-with-lease

答案 28 :(得分:3)

许多答案都基于 git rebase 命令,但根据我的经验,它对于 git 初学者来说有些复杂和高级。

假设您想压缩最后 3 次提交。然后是以下步骤:

  • 记下当前提交 id:使用 git log -1 --oneline 并记下当前状态的提交 id(以防万一您在 git reset 时做错了)
  • 返回 3 次提交:使用 git reset --soft HEAD~3 您将返回 3 次提交(并且有点忘记您之前已经进行了这 3 次提交)
  • 进行新的提交:现在只需执行 git commit -m <NEW_SINGLE_MESSAGE> 即可自动合并您的消息下的三个提交

如果 git reset 出现问题,您可以通过 git reset --soft <ORIGINAL_COMMIT>

再次恢复到原始状态

答案 29 :(得分:3)

如何使用Git将最后的X次提交一起压缩为一次提交?

git rebase -i HEAD~X

将显示以下内容:

pick 1bffc15c My earlier commit
pick 474bf0c2 My recent commit

# ...

对于您要压榨的提交,将 pick 替换为 fixup ,这样它将变为:

pick 1bffc15c My earlier commit
fixup 474bf0c2 My recent commit

# ...

如果它是在vim中打开的(终端中的默认界面),请按键盘上的Esc,分别输入:wqEnter保存文件。

验证:选中git log

答案 30 :(得分:2)

首先,我找出功能分支与当前主分支之间的提交次数

git checkout master
git rev-list master.. --count

然后,我根据我的功能分支创建另一个分支,保持my-feature分支不变。

最后,我跑

git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge

希望能有所帮助,谢谢。

答案 31 :(得分:2)

如果使用的是GitLab,则可以单击“合并请求”中的“壁球”选项,如下所示。提交消息将成为合并请求的标题。

enter image description here

答案 32 :(得分:2)

如果您不关心中间提交的提交消息,则可以使用

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend

答案 33 :(得分:1)

有没有人提到在 IntelliJ IDEA UI 上进行操作是多么容易:

  • 转到git窗口
  • 手动选择要合并为一个的所有提交。
  • 右键单击 > Squash Commits > 编辑压缩的提交消息
  • 点击左侧的分支名称 > 右键点击 > Push > Force Push

enter image description here

答案 34 :(得分:1)

如果您正在使用GitUp,请选择要与其父级合并的提交,然后按 S 。每次提交都必须执行一次,但它比提出正确的命令行咒语要简单得多。特别是如果它只是偶尔做一次。

答案 35 :(得分:1)

避免,在重新基于同一分支中的提交时必须解决任何合并冲突,可以使用以下命令

pick

要将所有提交压缩为一个,当系统提示您编辑要合并的提交时(-i标志),请按照其他答案中的建议,将除第一个操作以外的所有操作从squash更新为recursive

在这里,我们使用合并策略(-s标志)ours和策略选项(-X)git rebase -s ours,以确保历史记录中的后续提交赢得任何合并冲突。

注意:请勿将此与using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace iMouse { public partial class Events : Form { public bool TrackerSt; public FormMouseCR() { InitializeComponent(); } public void ButtonGetCoord_Click(object sender, EventArgs e) { Visible = false; TrackerSt = true; int x = Cursor.Position.X; int y = Cursor.Position.Y; int size = 10; // Arbitrary size System.Drawing.Graphics graphics = CreateGraphics(); System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(x - (size / 2), y - (size / 2), size, size); graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle); } private void FormMouseCR_MouseClick(object sender, MouseEventArgs e) { if(TrackerSt){ label1.Text = "X = " + e.X + " ; Y = " + e.Y; TrackerSt = false; } } private void FormMouseCR_MouseMove(object sender, MouseEventArgs e) { if(TrackerSt){ label1.Text = "X = " + e.X + " ; Y = " + e.Y; } } } } 混淆,后者会做其他事情。

参考:git rebase recursive merge strategy

答案 36 :(得分:0)

尝试了此处提到的所有方法。但最后,通过单击此链接,我的问题解决了。 https://gist.github.com/longtimeago/f7055aa4c3bba8a62197

$ git fetch upstream
$ git checkout omgpull 
$ git rebase -i upstream/master

 < choose squash for all of your commits, except the first one >
 < Edit the commit message to make sense, and describe all your changes >

$ git push origin omgpull -f

答案 37 :(得分:0)

1)git reset --soft HEAD〜2 && git commit -m“ 消息

您需要合并多少个提交,您需要下一个HEAD(例如HEAD〜2)

2)git push

将更改推送到特定分支

答案 38 :(得分:0)

例如,如果您要将所有提交压缩到分支(远程存储库)中的单个提交中,例如:https://bitbucket.org

我所做的是

  1. git reset --soft Head〜3 &&
  2. git commit
  3. git push origin --force

答案 39 :(得分:0)

只需将此bash函数添加到.zshrc文件的bash中。

# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
    if [ -z "${1}" -o -z "${2}" ]; then
        echo "Usage: \`squash X COMMIT_MSG\`"
        echo "X= Number of last commits."
        echo "COMMIT_MSG= New commit msg."
        return 1
    fi

    git reset --soft HEAD~"$1"
    git add . && git ci -m "$2" # With 100 emoji
    git push --force
}

然后运行

squash X 'New Commit Message'

你已经完成了。