Squash the Last X Commits Using Git – 使用Git压制最后的X个提交

最后修改: 2021年 8月 30日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

We can often hear the word “squash” when we talk about Git workflows.

当我们谈论Git工作流程时,我们经常可以听到 “压扁 “一词。

In this tutorial, we’ll briefly introduce what Git squashing is. Then we’ll talk about when we need to squash commits. Finally, we’ll take a closer look at how to squash commits.

在本教程中,我们将简要介绍什么是 Git 压扁。然后我们会讨论什么时候需要压扁提交。最后,我们将仔细研究如何压制提交。

2. What’s Git Squashing?

2. 什么是 Git Squashing? /span>

When we say “squash” in Git, it means to combine multiple continuous commits into one.

当我们在Git中说 “压制 “时,它意味着将多个连续提交合并为一个。

Let’s look at an example:

我们来看看一个例子。


          ┌───┐      ┌───┐     ┌───┐      ┌───┐
    ...   │ A │◄─────┤ B │◄────┤ C │◄─────┤ D │
          └───┘      └───┘     └───┘      └───┘

 After Squashing commits B, C, and D:

          ┌───┐      ┌───┐
    ...   │ A │◄─────┤ E │
          └───┘      └───┘

          ( The commit E includes the changes in B, C, and D.)

In this example, we’ve squashed the commits B, C and D into E.

在这个例子中,我们把B、C和D的提交压缩到E中。

Next, we’ll discuss when we should squash commits.

接下来,我们将讨论何时应该压制提交。

3. When to Squash Commits?

3.何时压制承诺?

Simply put, we use squashing to keep the branch graph clean.

简单地说,我们使用压扁法来保持分支图的清洁。

Let’s imagine how we implement a new feature. Usually, we’ll commit multiple times before we reach a satisfactory result, such as some fixes and tests.

让我们想象一下我们如何实现一个新的功能。通常情况下,我们会在达到一个满意的结果之前多次提交,比如一些修复和测试。

However, when we’ve implemented the feature, those intermediate commits look redundant. So, we may want to squash our commits into one.

但是,当我们实现了这个功能后,这些中间的提交就显得多余了。所以,我们可能想把我们的提交压缩成一个。

Another common scenario where we want to squash commits is merging branches.

另一个常见的情况是,我们要压制提交的情况是合并分支。

Very likely, when we start working on a new feature, we’ll start a feature branch. Let’s say we’ve done our work with 20 commits in our feature branch.

很有可能,当我们开始做一个新特性时,我们会启动一个特性分支。假设我们已经完成了我们的工作,在特性分支中有20个提交。

So, when we merge the feature branch to the master branch, we want to do a squashing to combine the 20 commits into one. This way, we keep the master branch clean.

所以,当我们把特性分支合并到主干分支时,我们要做一个压扁的动作,把20个提交合并成一个。这样一来,我们就能保持主干分支的干净。

4. How to Squash Commits?

4.如何压制承诺?

Today, some modern IDEs, such as IntelliJ and Eclipse, have integrated support for common Git operations. This allows us to squash commits from a GUI.

今天,一些现代的IDE,如IntelliJEclipse,已经集成了对常见Git操作的支持。这使我们能够从GUI中压制提交。

For example, in IntelliJ, we can select the commits we want to squash and choose “Squash Commits” in the right-click context menu:

例如,在IntelliJ中,我们可以选择我们想压制的提交,并在右键菜单中选择 “压制提交”。

However, in this tutorial, we’ll focus on squashing with Git commands.

然而,在本教程中,我们将专注于用Git命令进行压制。

We should note that squash is not a Git command, even if it’s a common Git operation. That is, “git squash … ” is an invalid Git command.

我们应该注意,squash不是一个Git命令,即使它是一个常见的Git操作。也就是说,”git squash …” 是一个无效的Git命令。

We’ll address two different approaches to squashing commits:

我们将讨论压制提交的两种不同方法。

Next, let’s see them in action.

接下来,让我们看看他们的行动。

5. Squashing by Interactive Rebase

5.通过交互式重设进行压制

Before we start, let’s create a Git alias slog (stands for short log) to show Git commit logs in a compact view:

在开始之前,让我们创建一个Git alias slog(代表简短的日志),以便在一个紧凑的视图中显示Git提交日志。

git config --global alias.slog = log --graph --all --topo-order --pretty='format:%h %ai %s%d (%an)'

We’ve prepared a Git repository as an example:

我们准备了一个Git仓库作为例子。

$ git slog
* ac7dd5f 2021-08-23 23:29:15 +0200 Commit D (HEAD -> master) (Kai Yuan)
* 5de0b6f 2021-08-23 23:29:08 +0200 Commit C (Kai Yuan)
* 54a204d 2021-08-23 23:29:02 +0200 Commit B (Kai Yuan)
* c407062 2021-08-23 23:28:56 +0200 Commit A (Kai Yuan)
* 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
* 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
* cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

Git’s interactive rebase will list all relevant commits in the default editor. In this case, those are the commits we want to squash.

Git的交互式重构会在默认编辑器中列出所有相关的提交。在这种情况下,这些就是我们要压制的提交。

Then we can control each commit and commit message as we want and save the change in the editor.

然后我们就可以按照自己的意愿控制每个提交和提交信息,并在编辑器中保存更改。

Next, let’s squash the last four commits.

接下来,让我们压制一下最后四个提交。

It’s worth mentioning that when we say “the last X commits,” we’re talking about the last X commits from the HEAD.

值得一提的是,当我们说 “最后的X提交 “时,我们说的是来自HEAD的最后X提交。

So, in this case, these are the last four commits:

因此,在这种情况下,这些是最后的四个承诺。

* ac7dd5f ... Commit D (HEAD -> master)
* 5de0b6f ... Commit C 
* 54a204d ... Commit B 
* c407062 ... Commit A

Moreover, if we’ve squashed already pushed commits, and we would like to publish the squashed result, we have to do a force push.

此外,如果我们压制了已经推送的提交,并且我们想发布压制的结果,我们必须进行强制推送。

It’s worth mentioning that force push to a public repository could be a dangerous operation, as it may overwrite others’ commits.

值得一提的是,强制推送到公共仓库可能是一个危险的操作,因为它可能会覆盖其他人的提交。

Further, when we really want to do a force push, we should make sure only to force push the required branch.

此外,当我们真的要进行强制推送时,我们应该确保只强制推送所需的分支。

For example, we can set the push.default property to current so that only the current branch will be pushed/force-pushed to the remote repository.

例如,我们可以push.default属性设置为current,这样就只有当前分支会被推送/强制推送到远程版本库。

Alternatively, we can force push to only one branch by adding a “+” in front of the refspec to push. For example, git push origin +feature will force a push to the feature branch.

另外,我们可以通过在要推送的refspec前面加一个 “+”来强制推送到一个分支。例如,git push origin +feature会强制推送到feature分支。

5.1. Squash the Last X Commits

5.1.压制最后的X承诺

Here’s the syntax to squash the last X commits using interactive rebase:

下面是使用交互式rebase压制最后X次提交的语法。

git rebase -i HEAD~[X]

So, this is what we should run:

因此,这就是我们应该运行的东西。

git rebase -i HEAD~4

After we execute the command, Git will start the system default editor (the Vim editor in this example) with the commits we want to squash and the interactive rebase help information:

在我们执行完命令后,Git 会启动系统默认的编辑器(本例中为 Vim 编辑器),并提供我们想要压制的提交内容和交互式 rebase 帮助信息。

As we can see in the screenshot above, all four commits we want to squash are listed in the editor with the pick command.

正如我们在上面的截图中所看到的,我们想要压制的四个提交都在编辑器中用pick命令列出。

There’s a detailed guideline on how to control each commit and commit message in the commented lines that follow.

在后面的注释行中,有一个关于如何控制每个提交和提交信息的详细指南。

For example, we can change the pick command of commits into s or squash to squash them:

例如,我们可以将提交的pick命令改为ssquash来压制它们。

If we save the change and exit the editor, Git will do the rebase following our instructions:

如果我们保存修改并退出编辑器,Git就会按照我们的指示进行重定位。

$ git rebase -i HEAD~4
[detached HEAD f9a9cd5] Commit A
 Date: Mon Aug 23 23:28:56 2021 +0200
 1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.

Now here’s what we’ll see if we check the Git commit log once again:

现在,如果我们再次查看Git提交日志,我们会看到以下情况。

$ git slog
* f9a9cd5 2021-08-23 23:28:56 +0200 Commit A (HEAD -> master) (Kai Yuan)
* 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
* 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
* cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

As the slog output shows, we’ve squashed the last four commits into one new commit, f9a9cd5​.

正如slog输出所显示的,我们已经把过去的四个提交压缩成一个新的提交,f9a9cd5

Now if we have a look at the complete log of the commit, we can see that the messages of all squashed commits are combined:

现在,如果我们看一下完整的提交日志,我们可以看到,所有被压扁的提交的信息都被合并了。

$ git log -1
commit f9a9cd50a0d11b6312ba4e6308698bea46e10cf1 (HEAD -> master)
Author: Kai Yuan
Date:   2021-08-23 23:28:56 +0200

    Commit A
    
    Commit B
    
    Commit C
    
    Commit D

5.2. When X Is Relatively Large

5.2.当X相对较大时

We’ve learned that the command git rebase -i HEAD~X is pretty straightforward to squash the last X commits.

我们已经了解到,命令git rebase -i HEAD~X 可以很直接地压制最后的X提交。

However, counting a larger X number can be a pain when we have quite a lot of commits in our branch. Moreover, it’s error-prone.

然而,当我们的分支中有相当多的提交时,计算一个较大的X数会很麻烦。此外,它还容易出错。

When the X is not easy to count, we can find the commit hash we want to rebase “onto” and run the command git rebase -i hash_onto.

X不容易计算时,我们可以找到我们想要重新建立 “到 “的提交哈希,然后运行git rebase -i hash_onto命令。

Let’s see how it works:

让我们看看它是如何工作的。

$ git slog
e7cb693 2021-08-24 15:00:56 +0200 Commit F (HEAD -> master) (Kai Yuan)
2c1aa63 2021-08-24 15:00:45 +0200 Commit E (Kai Yuan)
ac7dd5f 2021-08-23 23:29:15 +0200 Commit D (Kai Yuan)
5de0b6f 2021-08-23 23:29:08 +0200 Commit C (Kai Yuan)
54a204d 2021-08-23 23:29:02 +0200 Commit B (Kai Yuan)
c407062 2021-08-23 23:28:56 +0200 Commit A (Kai Yuan)
29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

As the git slog shows, in this branch, we have some commits.

正如git slog所示,在这个分支中,我们有一些提交。

Now let’s say we would like to squash all commits and rebase onto the commit 29976c5 with the message: BugFix #1.

现在,让我们说,我们想压制所有的提交,并重新建立在提交29976c5上,并写上信息。BugFix #1

So, we don’t have to count how many commits we need to squash. Instead, we can just execute the command git rebase -i 29976c5.

因此,我们不必计算我们需要压制多少个提交。相反,我们可以直接执行命令git rebase -i 29976c5.

We’ve learned that we need to change the pick commands into squash in the editor, and Git will do the squashing as we expected:

我们已经知道,我们需要把编辑器中的pick命令改为squash,Git就会像我们预期的那样进行压制。

$ git rebase -i 29976c5
[detached HEAD aabf37e] Commit A
 Date: Mon Aug 23 23:28:56 2021 +0200
 1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.

$ git slog
* aabf37e 2021-08-23 23:28:56 +0200 Commit A (HEAD -> master) (Kai Yuan)
* 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
* 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
* cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

6. Squashing by Merging With the –squash Option

6.用-squash选项合并来压扁

We’ve seen how to use Git interactive rebase to squash commits. This can effectively clean the commit-graph in a branch.

我们已经看到了如何使用 Git 交互式重定来压制提交。这可以有效地清理一个分支中的提交图。

However, we sometimes make many commits in our feature branch while working on it. After we’ve developed the feature, we usually want to merge the feature branch to the main branch, say “master”.

然而,我们有时会在特性分支上做很多提交,而在工作中。开发完功能后,我们通常想把功能分支合并到主分支,比如 “主”。

We want to keep the master branch graph clean, for example, one feature, one commit. But we don’t care about how many commits are in our feature branch.

我们希望保持主分支图的干净,例如,一个特性,一个提交。但我们并不关心我们的特性分支有多少个提交。

In this case, we can use the commit git merge –squash command to achieve that.

在这种情况下,我们可以使用提交git merge -squash命令来实现。

Let’s understand it through an example:

让我们通过一个例子来理解它。

$ git slog
* 0ff435a 2021-08-24 15:28:07 +0200 finally, it works. phew! (HEAD -> feature) (Kai Yuan)
* cb5fc72 2021-08-24 15:27:47 +0200 fix a typo (Kai Yuan)
* 251f01c 2021-08-24 15:27:38 +0200 fix a bug (Kai Yuan)
* e8e53d7 2021-08-24 15:27:13 +0200 implement Feature2 (Kai Yuan)
| * 204b03f 2021-08-24 15:30:29 +0200 Urgent HotFix2 (master) (Kai Yuan)
| * 8a58dd4 2021-08-24 15:30:15 +0200 Urgent HotFix1 (Kai Yuan)
|/  
* 172d2ed 2021-08-23 23:28:56 +0200 BugFix #2 (Kai Yuan)
* 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
* 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
* cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

As the output above shows, in this Git repository, we’ve implemented “Feature2” in the feature branch.

如上面的输出所示,在这个Git仓库中,我们在feature分支中实现了 “Feature2″。

In our feature branch, we’ve made four commits.

在我们的feature分支,我们做了四个提交。

Now we want to merge the result back to the master branch with one single commit to keep the master branch clean:

现在我们要将结果合并到master分支,只需一次提交,以保持master分支的清洁。

$ git checkout master
Switched to branch 'master'

$ git merge --squash feature
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

Unlike a regular merge, when we execute the command git merge with the –squash option, Git won’t automatically create a merge commit.

与普通的合并不同,当我们用-squash选项执行git merge命令时,Git不会自动创建一个合并提交。

Instead, it turns all changes from the source branch (the feature branch in this scenario) into local changes in the working copy:

相反,它将源分支(本方案中为feature分支)的所有修改变成工作副本中的本地修改

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   readme.md

In this example, all changes of the “Feature2” are about the readme.md file.

在这个例子中,”Feature2 “的所有修改都是关于readme.md文件的。

We need to commit the changes to complete the merge:

我们需要提交这些变化以完成合并。

$ git commit -am'Squashed and merged the Feature2 branch'
[master 565b254] Squashed and merged the Feature2 branch
 1 file changed, 4 insertions(+)

Now let’s check the branch graph:

现在我们来检查一下分支图。

$ git slog
* 565b254 2021-08-24 15:53:05 +0200 Squashed and merged the Feature2 branch (HEAD -> master) (Kai Yuan)
* 204b03f 2021-08-24 15:30:29 +0200 Urgent HotFix2 (Kai Yuan)
* 8a58dd4 2021-08-24 15:30:15 +0200 Urgent HotFix1 (Kai Yuan)
| * 0ff435a 2021-08-24 15:28:07 +0200 finally, it works. phew! (feature) (Kai Yuan)
| * cb5fc72 2021-08-24 15:27:47 +0200 fix a typo (Kai Yuan)
| * 251f01c 2021-08-24 15:27:38 +0200 fix a bug (Kai Yuan)
| * e8e53d7 2021-08-24 15:27:13 +0200 implement Feature2 (Kai Yuan)
|/  
* 172d2ed 2021-08-23 23:28:56 +0200 BugFix #2 (Kai Yuan)
* 29976c5 2021-08-23 23:28:33 +0200 BugFix #1 (Kai Yuan)
* 34fbfeb 2021-08-23 23:28:19 +0200 Feature1 implemented (Kai Yuan)
* cbd350d 2021-08-23 23:26:19 +0200 Init commit (Kai Yuan)

We can see that we’ve merged all the changes in the feature branch into the master branch, and we have one single commit, 565b254, in the master branch.

我们可以看到,我们已经将feature分支的所有修改合并到master分支,并且在master分支有一个单独的提交,565b254

On the other hand, in the feature branch, we still have four commits.

另一方面,在feature分支,我们仍有四个提交。

7. Conclusion

7.结语

In this article, we talked about what Git squashing is and when we should consider using it.

在这篇文章中,我们谈到了什么是Git压制,以及什么时候我们应该考虑使用它。

We also learned how to squash commits in Git.

我们还学习了如何在Git中压制提交。