这是用户在 2024-5-23 17:36 为 https://www.atlassian.com/git/tutorials/merging-vs-rebasing 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

Merging vs. rebasing  合并与变基

The git rebase command has a reputation for being magical Git hocus pocus that beginners should stay away from, but it can actually make life much easier for a development team when used with care. In this article, we’ll compare git rebase with the related git merge command and identify all of the potential opportunities to incorporate rebasing into the typical Git workflow.
git rebase 命令被誉为神奇的 Git 骗局,初学者应该远离它,但如果小心使用,它实际上可以让开发团队的工作变得更加轻松。在本文中,我们将比较 git rebase 与相关的 git merge 命令,并确定将变基合并到典型 Git 工作流程中的所有潜在机会。

Conceptual overview  概念概述

The first thing to understand about git rebase is that it solves the same problem as git merge. Both of these commands are designed to integrate changes from one branch into another branch—they just do it in very different ways.
关于 git rebase 首先要了解的是它解决了与 git merge 相同的问题。这两个命令都旨在将一个分支的更改集成到另一个分支中 - 它们只是以非常不同的方式进行。

Consider what happens when you start working on a new feature in a dedicated branch, then another team member updates the main branch with new commits. This results in a forked history, which should be familiar to anyone who has used Git as a collaboration tool.
考虑一下当您开始在专用分支中开发新功能,然后另一个团队成员用新提交更新 main 分支时会发生什么。这会产生分叉历史记录,任何使用过 Git 作为协作工具的人都应该熟悉这一点。

A forked commit history

Now, let’s say that the new commits in main are relevant to the feature that you’re working on. To incorporate the new commits into your feature branch, you have two options: merging or rebasing.
现在,假设 main 中的新提交与您正在开发的功能相关。要将新提交合并到您的 feature 分支中,您有两个选择:合并或变基。

related material  相关材料

How to move a full Git repository
如何移动完整的 Git 存储库

Bitbucket logo

Learn Git with Bitbucket Cloud
使用 Bitbucket Cloud 学习 Git

The merge option  合并选项

The easiest option is to merge the main branch into the feature branch using something like the following:
最简单的选择是使用类似以下内容将 main 分支合并到功能分支中:

git checkout feature
git merge main

Or, you can condense this to a one-liner:

git merge feature main

This creates a new “merge commit” in the feature branch that ties together the histories of both branches, giving you a branch structure that looks like this:
这会在 feature 分支中创建一个新的“合并提交”,将两个分支的历史记录联系在一起,为您提供如下所示的分支结构:

Merging main into feature branch

Merging is nice because it’s a non-destructive operation. The existing branches are not changed in any way. This avoids all of the potential pitfalls of rebasing (discussed below).

On the other hand, this also means that the feature branch will have an extraneous merge commit every time you need to incorporate upstream changes. If main is very active, this can pollute your feature branch’s history quite a bit. While it’s possible to mitigate this issue with advanced git log options, it can make it hard for other developers to understand the history of the project.
另一方面,这也意味着每次需要合并上游更改时, feature 分支都会有一个无关的合并提交。如果 main 非常活跃,这可能会严重污染你的功能分支的历史记录。虽然可以使用高级 git log 选项来缓解此问题,但它可能会使其他开发人员难以理解项目的历史。

The rebase option  变基选项

As an alternative to merging, you can rebase the feature branch onto main branch using the following commands:
作为合并的替代方法,您可以使用以下命令将 feature 分支重新设置为 main 分支:

git checkout feature
git rebase main

This moves the entire feature branch to begin on the tip of the main branch, effectively incorporating all of the new commits in main. But, instead of using a merge commit, rebasing re-writes the project history by creating brand new commits for each commit in the original branch.
这会将整个 feature 分支移至 main 分支的顶端,从而有效地将所有新提交合并到 main 中。但是,变基不是使用合并提交,而是通过为原始分支中的每个提交创建全新的提交来重写项目历史记录。

Rebasing feature branch into main

The major benefit of rebasing is that you get a much cleaner project history. First, it eliminates the unnecessary merge commits required by git merge. Second, as you can see in the above diagram, rebasing also results in a perfectly linear project history—you can follow the tip of feature all the way to the beginning of the project without any forks. This makes it easier to navigate your project with commands like git log, git bisect, and gitk.
变基的主要好处是您可以获得更清晰的项目历史记录。首先,它消除了 git merge 所需的不必要的合并提交。其次,正如您在上图中所看到的,变基还会产生完美的线性项目历史记录 - 您可以按照 feature 的提示一直到项目的开头,而无需任何分叉。这使得使用 git loggit bisectgitk 等命令更轻松地导航您的项目。

But, there are two trade-offs for this pristine commit history: safety and traceability. If you don’t follow the Golden Rule of Rebasing, re-writing project history can be potentially catastrophic for your collaboration workflow. And, less importantly, rebasing loses the context provided by a merge commit—you can’t see when upstream changes were incorporated into the feature.
但是,对于这个原始的提交历史有两个权衡:安全性和可追溯性。如果您不遵循变基黄金法则,重写项目历史记录可能会给您的协作工作流程带来灾难性的后果。而且,不太重要的是,变基会丢失合并提交提供的上下文 - 您无法看到上游更改何时合并到功能中。

Interactive rebasing  交互式变基

Interactive rebasing gives you the opportunity to alter commits as they are moved to the new branch. This is even more powerful than an automated rebase, since it offers complete control over the branch’s commit history. Typically, this is used to clean up a messy history before merging a feature branch into main.
交互式变基使您有机会在提交移至新分支时更改提交。这比自动变基更强大,因为它提供了对分支提交历史记录的完全控制。通常,这用于在将功能分支合并到 main 之前清理混乱的历史记录。

To begin an interactive rebasing session, pass the i option to the git rebase command:
要开始交互式变基会话,请将 i 选项传递给 git rebase 命令:

git checkout feature
git rebase -i main

This will open a text editor listing all of the commits that are about to be moved:

pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

This listing defines exactly what the branch will look like after the rebase is performed. By changing the pick command and/or re-ordering the entries, you can make the branch’s history look like whatever you want. For example, if the 2nd commit fixes a small problem in the 1st commit, you can condense them into a single commit with the fixup command:
此清单准确定义了执行变基后分支的外观。通过更改 pick 命令和/或重新排序条目,您可以使分支的历史记录看起来像您想要的那样。例如,如果第二次提交修复了第一次提交中的小问题,您可以使用 fixup 命令将它们压缩为单个提交:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

When you save and close the file, Git will perform the rebase according to your instructions, resulting in project history that looks like the following:
当您保存并关闭文件时,Git 将根据您的指示执行变基,从而生成如下所示的项目历史记录:

Squashing a commit with an interactive rebase

Eliminating insignificant commits like this makes your feature’s history much easier to understand. This is something that git merge simply cannot do.
消除像这样无关紧要的提交可以使你的功能的历史更容易理解。这是 git merge 根本做不到的。

The golden rule of rebasing

Once you understand what rebasing is, the most important thing to learn is when not to do it. The golden rule of git rebase is to never use it on public branches.
一旦您了解了变基是什么,最重要的是要了解什么时候不应该这样做。 git rebase 的黄金法则是永远不要在公共分支上使用它。

For example, think about what would happen if you rebased main onto your feature branch:
例如,考虑一下如果您将 main 重新设置为 feature 分支,会发生什么情况:

Rebasing the main branch

The rebase moves all of the commits in main onto the tip of feature. The problem is that this only happened in your repository. All of the other developers are still working with the original main. Since rebasing results in brand new commits, Git will think that your main branch’s history has diverged from everybody else’s.
变基将 main 中的所有提交移动到 feature 的尖端。问题是这只发生在您的存储库中。所有其他开发人员仍在使用原始的 main 。由于变基会导致全新的提交,Git 会认为您的 main 分支的历史记录与其他分支的历史记录有所不同。

The only way to synchronize the two main branches is to merge them back together, resulting in an extra merge commit and two sets of commits that contain the same changes (the original ones, and the ones from your rebased branch). Needless to say, this is a very confusing situation.
同步两个 main 分支的唯一方法是将它们合并在一起,从而产生一个额外的合并提交和两组包含相同更改的提交(原始提交和来自重新基准分支的提交)。不用说,这是一个非常令人困惑的情况。

So, before you run git rebase, always ask yourself, “Is anyone else looking at this branch?” If the answer is yes, take your hands off the keyboard and start thinking about a non-destructive way to make your changes (e.g., the git revert command). Otherwise, you’re safe to re-write history as much as you like.
所以,在运行 git rebase 之前,一定要问自己,“还有其他人在看这个分支吗?”如果答案是肯定的,请将手从键盘上移开,并开始考虑一种非破坏性的方式来进行更改(例如, git revert 命令)。否则,你可以随心所欲地重写历史。

Force-pushing  力推式

If you try to push the rebased main branch back to a remote repository, Git will prevent you from doing so because it conflicts with the remote main branch. But, you can force the push to go through by passing the --force flag, like so:
如果您尝试将重新基址的 main 分支推送回远程存储库,Git 将阻止您这样做,因为它与远程 main 分支冲突。但是,您可以通过传递 --force 标志来强制推送,如下所示:

# Be very careful with this command! git push --force

This overwrites the remote main branch to match the rebased one from your repository and makes things very confusing for the rest of your team. So, be very careful to use this command only when you know exactly what you’re doing.
这会覆盖远程 main 分支以匹配存储库中重新设置的分支,并使团队的其他成员感到非常困惑。因此,只有当您确切知道自己在做什么时,才要非常小心地使用此命令。

One of the only times you should be force-pushing is when you’ve performed a local cleanup after you’ve pushed a private feature branch to a remote repository (e.g., for backup purposes). This is like saying, “Oops, I didn’t really want to push that original version of the feature branch. Take the current one instead.” Again, it’s important that nobody is working off of the commits from the original version of the feature branch.

Workflow walkthrough  工作流程演练

Rebasing can be incorporated into your existing Git workflow as much or as little as your team is comfortable with. In this section, we’ll take a look at the benefits that rebasing can offer at the various stages of a feature’s development.
根据您的团队的喜好,可以将变基合并到您现有的 Git 工作流程中。在本节中,我们将了解变基在功能开发的各个阶段可以提供的好处。

The first step in any workflow that leverages git rebase is to create a dedicated branch for each feature. This gives you the necessary branch structure to safely utilize rebasing:
任何利用 git rebase 的工作流程的第一步是为每个功能创建专用分支。这为您提供了安全利用变基所需的分支结构:

Developing a feature in a dedicated branch

Local cleanup  局部清理

One of the best ways to incorporate rebasing into your workflow is to clean up local, in-progress features. By periodically performing an interactive rebase, you can make sure each commit in your feature is focused and meaningful. This lets you write your code without worrying about breaking it up into isolated commits—you can fix it up after the fact.
将变基合并到工作流程中的最佳方法之一是清理本地正在进行的功能。通过定期执行交互式变基,您可以确保功能中的每次提交都有重点且有意义。这使您可以编写代码,而不必担心将其分解为单独的提交 - 您可以在事后修复它。

When calling git rebase, you have two options for the new base: The feature’s parent branch (e.g., main), or an earlier commit in your feature. We saw an example of the first option in the Interactive Rebasing section. The latter option is nice when you only need to fix up the last few commits. For example, the following command begins an interactive rebase of only the last 3 commits.
调用 git rebase 时,新基础有两个选项:功能的父分支(例如 main ),或功能中较早的提交。我们在交互式变基部分看到了第一个选项的示例。当您只需要修复最后几次提交时,后一个选项很好。例如,以下命令开始仅对最后 3 次提交进行交互式变基。

git checkout feature git rebase -i HEAD~3

By specifying HEAD~3 as the new base, you’re not actually moving the branch—you’re just interactively re-writing the 3 commits that follow it. Note that this will not incorporate upstream changes into the feature branch.
通过指定 HEAD~3 作为新的基础,你实际上并没有移动分支——你只是以交互方式重写了它后面的 3 个提交。请注意,这不会将上游更改合并到 feature 分支中。

Rebasing onto Head~3

If you want to re-write the entire feature using this method, the git merge-base command can be useful to find the original base of the feature branch. The following returns the commit ID of the original base, which you can then pass to git rebase:
如果您想使用此方法重写整个功能, git merge-base 命令可用于查找 feature 分支的原始基础。以下返回原始基础的提交 ID,然后您可以将其传递给 git rebase

git merge-base feature main

This use of interactive rebasing is a great way to introduce git rebase into your workflow, as it only affects local branches. The only thing other developers will see is your finished product, which should be a clean, easy-to-follow feature branch history.
这种交互式变基的使用是将 git rebase 引入工作流程的好方法,因为它只影响本地分支。其他开发人员唯一能看到的是你的成品,它应该是一个干净、易于理解的功能分支历史记录。

But again, this only works for private feature branches. If you’re collaborating with other developers via the same feature branch, that branch is public, and you’re not allowed to re-write its history.

There is no git merge alternative for cleaning up local commits with an interactive rebase.
没有 git merge 替代方案可以通过交互式变基来清理本地提交。

Incorporating upstream changes into a feature

In the Conceptual Overview section, we saw how a feature branch can incorporate upstream changes from main using either git merge or git rebase. Merging is a safe option that preserves the entire history of your repository, while rebasing creates a linear history by moving your feature branch onto the tip of main.
在概念概述部分中,我们了解了功能分支如何使用 git mergegit rebase 合并来自 main 的上游更改。合并是一个安全的选项,可以保留存储库的整个历史记录,而变基则通过将功能分支移动到 main 的尖端来创建线性历史记录。

This use of git rebase is similar to a local cleanup (and can be performed simultaneously), but in the process it incorporates those upstream commits from main.
git rebase 的这种使用类似于本地清理(并且可以同时执行),但在此过程中它合并了来自 main 的上游提交。

Keep in mind that it’s perfectly legal to rebase onto a remote branch instead of main. This can happen when collaborating on the same feature with another developer and you need to incorporate their changes into your repository.
请记住,变基到远程分支而不是 main 是完全合法的。当与其他开发人员协作开发同一功能并且您需要将他们的更改合并到您的存储库时,可能会发生这种情况。

For example, if you and another developer named John added commits to the feature branch, your repository might look like the following after fetching the remote feature branch from John’s repository:
例如,如果您和另一位名为 John 的开发人员向 feature 分支添加了提交,则从 John 的存储库获取远程 feature 分支后,您的存储库可能如下所示:

Collaborating on the same feature branch

You can resolve this fork the exact same way as you integrate upstream changes from main: either merge your local feature with john/feature, or rebase your local feature onto the tip of john/feature.
您可以按照与从 main 集成上游更改完全相同的方式解决此分叉:将本地 featurejohn/feature 合并,或者将本地 feature 重新设置为 john/feature 的尖端。

Merging vs. rebasing onto a remote branch

Note that this rebase doesn’t violate the Golden Rule of Rebasing because only your local feature commits are being moved—everything before that is untouched. This is like saying, “add my changes to what John has already done.” In most circumstances, this is more intuitive than synchronizing with the remote branch via a merge commit.
请注意,此变基并不违反变基黄金法则,因为只有本地 feature 提交被移动 - 之前的所有内容都保持不变。这就像说,“将我的更改添加到约翰已经完成的内容中。”在大多数情况下,这比通过合并提交与远程分支同步更直观。

By default, the git pull command performs a merge, but you can force it to integrate the remote branch with a rebase by passing it the --rebase option.
默认情况下, git pull 命令执行合并,但您可以通过向其传递 --rebase 选项来强制它将远程分支与变基集成。

Reviewing a feature with a pull request

If you use pull requests as part of your code review process, you need to avoid using git rebase after creating the pull request. As soon as you make the pull request, other developers will be looking at your commits, which means that it’s a public branch. Re-writing its history will make it impossible for Git and your teammates to track any follow-up commits added to the feature.
如果您使用拉取请求作为代码审查过程的一部分,则需要在创建拉取请求后避免使用 git rebase 。一旦您发出拉取请求,其他开发人员就会查看您的提交,这意味着它是一个公共分支。重写其历史记录将使 Git 和您的团队成员无法跟踪添加到该功能的任何后续提交。

Any changes from other developers need to be incorporated with git merge instead of git rebase.
其他开发人员的任何更改都需要与 git merge 而不是 git rebase 合并。

For this reason, it’s usually a good idea to clean up your code with an interactive rebase before submitting your pull request.

Integrating an approved feature

After a feature has been approved by your team, you have the option of rebasing the feature onto the tip of the main branch before using git merge to integrate the feature into the main code base.
在您的团队批准某个功能后,您可以选择将该功能重新设置为 main 分支的顶端,然后再使用 git merge 将该功能集成到主代码库中。

This is a similar situation to incorporating upstream changes into a feature branch, but since you’re not allowed to re-write commits in the main branch, you have to eventually use git merge to integrate the feature. However, by performing a rebase before the merge, you’re assured that the merge will be fast-forwarded, resulting in a perfectly linear history. This also gives you the chance to squash any follow-up commits added during a pull request.
这与将上游更改合并到功能分支中类似,但由于不允许在 main 分支中重写提交,因此最终必须使用 git merge 来集成该功能。但是,通过在合并之前执行变基,您可以确保合并将快进,从而产生完美的线性历史记录。这也使您有机会压缩在拉取请求期间添加的任何后续提交。

Integrating a feature into main with and without a rebase

If you’re not entirely comfortable with git rebase, you can always perform the rebase in a temporary branch. That way, if you accidentally mess up your feature’s history, you can check out the original branch and try again. For example:
如果您对 git rebase 不太满意,您始终可以在临时分支中执行变基。这样,如果您不小心弄乱了功能的历史记录,您可以检查原始分支并重试。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i main[Clean up the history]
git checkout main
git merge temporary-branch

Summary  概括

And that’s all you really need to know to start rebasing your branches. If you would prefer a clean, linear history free of unnecessary merge commits, you should reach for git rebase instead of git merge when integrating changes from another branch.
这就是开始重新调整分支基础所需的全部信息。如果您希望获得干净、线性的历史记录,避免不必要的合并提交,那么在集成来自另一个分支的更改时,您应该使用 git rebase 而不是 git merge

On the other hand, if you want to preserve the complete history of your project and avoid the risk of re-writing public commits, you can stick with git merge. Either option is perfectly valid, but at least now you have the option of leveraging the benefits of git rebase.
另一方面,如果您想保留项目的完整历史记录并避免重写公共提交的风险,您可以坚持使用 git merge 。这两个选项都是完全有效的,但至少现在您可以选择利用 git rebase 的好处。

Share this article  分享此文章

Recommended reading  推荐阅读

Bookmark these resources to learn about types of DevOps teams, or for ongoing updates about DevOps at Atlassian.
为这些资源添加书签,以了解 DevOps 团队的类型,或了解有关 Atlassian DevOps 的持续更新。

People collaborating using a wall full of tools

Bitbucket blog

Devops illustration

DevOps learning path

Demo Den Feature demos with Atlassian experts

How Bitbucket Cloud works with Atlassian Open DevOps

Sign up for our DevOps newsletter

Thank you for signing up

Drag to outliner or Upload