Merging vs. Rebasing 合并与重排
While merging and rebasing are similar in Git, they serve two different functions. Here's what you should know to keep your histories as clean or as complete as you like.
虽然合并和重定向在 Git 中很相似,但它们却有两种不同的功能。下面是你应该知道的,让你的历史记录保持干净或完整。
Join the DZone community and get the full member experience.
Join For Free加入 DZone 社区,获得完整的会员体验。免费加入
The git rebase
command has a reputation for being magical Git voodoo 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 master
branch with new commits. This results in a forked history, which should be familiar to anyone who has used Git as a collaboration tool.
想想看,当你在一个专用分支中开始开发一项新功能时,另一个团队成员又在 master
分支中更新了新提交,这会发生什么?使用过 Git 作为协作工具的人应该不会对这种情况感到陌生。
Now, let’s say that the new commits in master
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.
现在,假设 master
中的新提交与您正在开发的功能相关。要将新提交合并到 feature
分支中,有两个选择:合并或重定向。
The Merge Option 合并选项
The easiest option is to merge the master
branch into the feature branch using something like the following:
最简单的方法是将 master
分支合并到功能分支,具体做法如下:
git checkout feature
git merge master
Or, you can condense this to a one-liner:
或者,你也可以用一句话来概括:
git merge master feature
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 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 master
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
分支在每次需要纳入上游变更时,都会有一个无关的合并提交。如果 master
非常活跃,这就会严重污染特性分支的历史记录。虽然可以通过高级 git log
选项来缓解这一问题,但这会让其他开发人员很难了解项目的历史。
The Rebase Option 重置选项
As an alternative to merging, you can rebase the feature
branch onto master
branch using the following commands:
作为合并的替代方法,您可以使用以下命令将 feature
分支重定向到 master
分支:
git checkout feature
git rebase master
This moves the entire feature
branch to begin on the tip of the master
branch, effectively incorporating all of the new commits in master
. But, instead of using a merge commit, rebasing re-writesthe project history by creating brand new commits for each commit in the original branch.
这样,整个 feature
分支就会从 master
分支的顶端开始,从而有效合并了 master
中的所有新提交。不过,重定向不是使用合并提交,而是为原始分支中的每个提交创建全新的提交,从而重写项目历史。
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 log
、 git bisect
和 gitk
等命令浏览项目变得更加容易。
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 master
.
交互式重置让你有机会在提交移动到新分支时对其进行修改。交互式重定向比自动重定向更强大,因为它能完全控制分支的提交历史。通常情况下,在将特性分支合并到 master
之前,会使用这种方法来清理杂乱的历史记录。
To begin an interactive rebasing session, pass the i
option to thegit rebase
command:
要开始交互式重定向会话,请向 git rebase
命令传递 i
选项:
git checkout feature
git rebase -i master
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
命令和/或重新排列条目,你可以让分支的历史看起来像你想要的任何样子。例如,如果第 2 次提交修复了第 1 次提交中的一个小问题,就可以使用 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 会根据你的指示执行重置操作,项目历史记录如下:
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 master
onto your feature
branch:
例如,想想如果将 master
重定向到 feature
分支上会发生什么:
The rebase moves all of the commits in master
onto the tip of feature
. The problem is that this only happened in yourrepository. All of the other developers are still working with the original master
. Since rebasing results in brand new commits, Git will think that your master
branch’s history has diverged from everybody else’s.
rebase 会将 master
中的所有提交移到 feature
的顶端。问题是,这只发生在你的版本库中。所有其他开发人员仍在使用原来的 master
。由于重定向会产生全新的提交,Git 会认为你的 master
分支的历史与其他人的不同。
The only way to synchronize the two master
branches is to merge them back together, resulting in an extra merge commitand 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.
同步两个 master
分支的唯一方法就是将它们合并到一起,这样就会产生一个额外的合并提交和两组包含相同变更的提交(原始提交和从您的分支中重建的提交)。不用说,这种情况会非常混乱。
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 master
branch back to a remote repository, Git will prevent you from doing so because it conflicts with the remote master
branch. But, you can force the push to go through by passing the --force
flag, like so:
如果你想把重建后的 master
分支推送回远程仓库,Git 会阻止你这么做,因为它与远程的 master
分支冲突。不过,你可以通过 --force
标志来强制推送,就像这样:
# Be very careful with this command!
git push --force
This overwrites the remote master
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.
这会覆盖远程 master
分支,使其与版本库中重建的分支相匹配,这会让团队其他成员感到非常困惑。因此,只有在你非常清楚自己在做什么的情况下,才要非常小心地使用这条命令。
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
的工作流程的第一步都是为每个功能创建一个专用分支。这将为您提供必要的分支结构,以便安全地使用重定向:
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., master
), or an earlier commit in your feature. We saw an example of the first option in theInteractive 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
时,您有两个选择:功能的父分支(例如 master
),或者功能中较早的提交。我们在 "交互式重定向 "部分看到了第一个选项的示例。当你只需要修复最后几次提交时,后一个选项就很不错。例如,下面的命令只对最后 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
分支。
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 master
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
方法可以通过交互式 rebase 来清理本地提交。
Incorporating Upstream Changes Into a Feature
将上游更改纳入功能
In the Conceptual Overview section, we saw how a feature branch can incorporate upstream changes from master
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 master
.
在 "概念概述 "部分,我们看到了特性分支如何使用 git merge
或 git rebase
来合并来自 master
的上游变更。合并是一个安全的选择,可以保留版本库的整个历史,而重定向则通过将功能分支移动到 master
的顶端来创建线性历史。
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 master
.
git rebase
的使用类似于本地清理(可同时执行),但在此过程中会纳入来自 master
的上游提交。
Keep in mind that it’s perfectly legal to rebase onto a remote branch instead of master
. This can happen when collaborating on the same feature with another developer and you need to incorporate their changes into your repository.
请记住,将 master
重置到远程分支上是完全合法的。在与其他开发人员合作开发同一功能时,如果需要将他们的修改纳入自己的版本库,就可能发生这种情况。
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
分支后,您的版本库可能会如下所示:
You can resolve this fork the exact same way as you integrate upstream changes from master
: either merge your local feature
with john/feature
, or rebase your local feature
onto the tip of john/feature
.
您可以采用与整合 master
上游更改完全相同的方式来解决此分叉问题:要么将本地 feature
与 john/feature
合并,要么将本地 feature
重定向到 john/feature
的顶端。
Note that this rebase doesn’t violate the Golden Rule of Rebasingbecause 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
提交被移动,之前的所有提交都不会被移动。这就好比说,"把我的改动添加到 John 已经完成的改动中"。在大多数情况下,这比通过合并提交与远程分支同步更直观。
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
选项,强制它以 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 master
branch before using git merge
to integrate the feature into the main code base.
在功能获得团队批准后,您可以选择将功能重定向到 master
分支的顶端,然后再使用 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 master
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.
这种情况与将上游变更纳入特性分支类似,但由于不允许重写 master
分支中的提交,所以最终必须使用 git merge
来整合特性。不过,如果在合并前执行重置,就能确保合并是快进的,从而实现完美的线性历史。这也为您提供了一个机会,可以压制在拉取请求中添加的任何后续提交。
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 master
# [Clean up the history]
git checkout master
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
的优势。
分支(计算机科学) Commit (data management)
承诺(数据管理) Git History (command) 历史记录(命令) Command (computing) 指挥(计算) workplace 工作场所 dev 开发 Upstream (software development)
上游(软件开发)
Published at DZone with permission of Tim Pettersen, DZone MVB.
See the original article here.
经 DZone MVB Tim Pettersen 许可发布于 DZone。点击此处查看原文。
Opinions expressed by DZone contributors are their own.
DZone 撰稿人的观点仅代表其本人。
Comments