原文: https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing
目录
Conceptual Overview – 概念概述 The Golden Rule of Rebasing – rebase的黄金法则 Workflow Walkthrough – 工作流程 Summary – 总结
The git rebase commandhas a reputation for being magical Git voodoo that beginners should stay awayfrom, but it can actually make life much easier for a development team whenused with care. In this article, we’ll compare git rebase withthe related git merge command and identify all of thepotential opportunities to incorporate rebasing into the typical Git workflow.
`git rebase` 命令是一种让初学者望而却步的Git神技,但其实运用得当的话,会让开发团队的日子好过许多。在本文中,我们将对它和 `git merge` 命令进行比较,并找出在典型的 Git 工作流中应用 rebase 的所有潜在机会。
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 tointegrate changes from one branch into another branch—they just do it in verydifferent ways.
首先要理解的是,`git rebase` 和 `git merge` 解决的是同一件事 – 将一个分支的改变融入另一个分支中,只是他们实现的方式大相径庭罢了。
Considerwhat happens when you start working on a new feature in a dedicated branch,then another team member updates the master branchwith new commits. This results in a forked history, which should be familiar toanyone who has used Git as a collaboration tool.
比如当你在一个专用的分支上提交了某些新的特性,而团队中其他成员向master贡献了若干commit,这就导致了提交历史的“分岔” – 这也是将 Git 作为协同工具的开发者都熟悉的场景。
(一个分岔的提交历史)
Now,let’s say that the new commits in master arerelevant to the feature that you’re working on. To incorporate the new commitsinto your feature branch, you have two options: mergingor rebasing.
这样两方的commit就有了相关性,而要合并它们,就要在 `git rebase` 和`git merge` 中进行选择了。
The Merge Option [mereg选项]
Theeasiest option is to merge the master branch into thefeature 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
Thiscreates a new “merge commit” in the feature branchthat ties together the histories of both branches, giving you a branchstructure that looks like this:
这样会在目标分支上创建一个新的 “merge commit”,从而将两个分支的历史连接起来,形成如下结构:
(将master合并到feature时产生的 merge commit)
Merging is nice because it’s a non-destructive operation. The existingbranches are not changed in any way. This avoids all of the potential pitfallsof rebasing (discussed below).
merge的好处就在于其非破坏性,已存在的分支完全不会被改变;这样就避免了 rebase 操作的所有潜在风险(下面会讨论到)。
On the other hand, this also means that the feature branchwill have an extraneous merge commit every time you need to incorporateupstream changes. If master is very active, this can pollute yourfeature branch’s history quite a bit. While it’s possible to mitigate thisissue with advanced git log options, it can make it hard forother developers to understand the history of the project.
另一方面,这也意味着每次合并会在目标分支上产生一个多余无用的merge commit。如果master 需要经常更改,那就会很大程度的污染目标分支的提交历史。虽然也可以通过 git log 的高级选项减轻这种影响,但还是会对其他开发者理解提交历史产生困扰。
The Rebase Option [ rebase选项]
As an alternative to merging, you can rebase the feature branchonto master branch using the following commands:
作为merge 的替代选择,可以像下面这样把目标分支rebase到master:
git checkout feature git rebase master
This moves the entire feature branch to begin on the tip ofthe master branch, effectively incorporating allof the new commits in master. But, instead of using a merge commit, rebasing re-writestheproject history by creating brand new commits for each commit in the originalbranch.
这将把整个目标分支移至master的开头端,实际上就是将所有新的commit融入master。但是,区别于使用一个merge commit 的做法,rebase把原始分支中的每次commit重新提交到目标分支,从而重写了提交历史。
(把目标分支rebase到master)
The majorbenefit of rebasing is that you get a much cleaner project history. First, iteliminates the unnecessary merge commits required by gitmerge.Second, as you can see in the above diagram, rebasing also results in aperfectly linear project history—you can follow the tip of feature allthe way to the beginning of the project without any forks. This makes it easierto navigate your project with commands like git log, gitbisect,and gitk.
rebase最大的好处就是提交历史很清晰。首先它排除了多余的 merge commit,其次,如上图所示,rebase形成了一个完美的线性历史,可以从头到尾一路追踪改动,而不用担心任何分岔的出现。这也就使得用 git log | git bisect |gitk 等命令来追踪历史变得容易。
But,there are two trade-offs for this pristine commit history: safety andtraceability. If you don’t follow the Golden Rule of Rebasing,re-writing project history can be potentially catastrophic for yourcollaboration workflow. And, less importantly, rebasing loses the contextprovided by a merge commit—you can’t see when upstream changes wereincorporated into the feature.
但对于这个全新的提交历史,也有两点需要取舍:安全性和可追溯性。如果不按照下面将要介绍的“rebase黄金法则”进行,重写历史可能会是团队协作开发的灾难。其次,没那么重要的是,rebase丢失了merge commit提供的上下文 – 无法看懂何时融入了新特性。
Interactive Rebasing [交互式的rebase]
Interactiverebasing gives you the opportunity to alter commits as they are moved to thenew branch. This is even more powerful than an automated rebase, since itoffers complete control over the branch’s commit history. Typically, this isused to clean up a messy history before merging a feature branch into master.
交互式rebase提供了对已移动到新分支的commit进行更改的机会。因为可以完全控制分支的提交历史,所以比自动rebase更加强大。典型的作用是,在目标分支合并到master之前,将混乱的历史清除干净。
To begin an interactive rebasing session, pass the i optionto the git rebase command:
使用 -i 参数开始交互式的 git rebase 命令:
git checkout feature git rebase -i master
This will open a text editor listing all of the commits that are about to be moved:
这将打开一个文本编辑器,并列出所有将要被移动的commit:
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 isperformed. By changing the pick command and/or re-ordering theentries, you can make the branch’s history look like whatever you want. Forexample, if the 2nd commit fixes a small problem in the 1st commit, you cancondense them into a single commit with the fixup command:
该列表确切的定义了rebase执行之后分支的样貌。可以自由的编辑列表,从而改变pick命令,或重新排序条目。比如,如果#2提交只是对#1提交的小幅修正,就可以用fixup命令将这两次提交压缩成一次commit。
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 yourinstructions, resulting in project history that looks like the following:
当该文件被保存并关闭,Git就将按这个顺序执行rebase,形成如下提交历史:
Eliminating insignificant commits like this makes your feature’s history much easier tounderstand. This is something that git merge simplycannot do.
如此将一些无足轻重的commit排除掉,使得提交历史简单易懂;而用git merge是无法轻易做到的。
(bash on ubuntu@win10 中编辑器的样子)
The Golden Rule of Rebasing [rebase的黄金法则]
Once you understand what rebasing is, the most important thing to learn iswhen not to do it. The golden rule of gitrebase isto never use it on public branches.
一旦理解了rebase是什么,接下来最重要的事情就是去学习它,而非马上动手。关于rebase的黄金法则就是:永远不要在公开分支上使用它。
For example, think about what would happen if you rebased master ontoyour feature branch:
比如,设想一下如果master被rebase到你的目标分支上,会发生什么:
The rebase moves all of the commits in master ontothe tip of feature. The problem is that this only happenedin yourrepository. All of the other developers are still workingwith the original master. Since rebasing results in brand newcommits, Git will think that your master branch’shistory has diverged from everybody else’s.
rebase将master中的所有commit都移动到了目标分支的头部;而这一切只发生在你的仓库中,其他人则继续在原来的master上进行开发 – 因为rebase形成了新的提交,所以Git会认为你的master分支和其他人的分岔了。
The only way to synchronize the two master branches is tomerge them back together, resulting in an extra merge commit and twosets of commits that contain the same changes (the original ones, and the onesfrom your rebased branch). Needless to say, this is a very confusing situation.
此时要想修正这种情况,从而保持两个master分支同步的唯一办法,就是再讲它们merge回去一边,这无疑会造就一个多余的merge commit,并且两组提交包含了同样的改变(一边是原始的,另一边是rebase分支上的那套)。显然这就尴尬了~
So,before you run git rebase, always ask yourself, “Is anyone elselooking at this branch?” If the answer is yes, take your hands off the keyboardand start thinking about a non-destructive way to make your changes (e.g.,the git revert command). Otherwise, you’re safe tore-write history as much as you like.
所以在每次git rebase之前,扪心自问一下:“有其他小伙伴也在盯着这个分支吗?”。如果答案是yes,那么把你的双手从键盘上拿开,想想是否有一种非破坏性的方式达成改变(比如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:
如果试图将rebase后的master分支push回远端仓库的话,Git会阻止这种行为,以防和远端的master发生冲突。此时可以用 --flag参数强行执行:
# 慎用此命令! git push --force
This overwrites the remote master branch to match the rebased one fromyour 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’redoing.
这将覆盖远端master分支,所以只有在你清楚的知道自己所做之事时,才能慎重的使用该命令,以免给其他团队成员带来麻烦。
One of the only times you should be force-pushing is when you’ve performed a localcleanup after you’ve pushed a private feature branch to aremote repository (e.g., for backup purposes). This is like saying, “Oops, Ididn’t really want to push that original version of the feature branch. Takethe current one instead.” Again, it’s important that nobody is working off ofthe commits from the original version of the feature branch.
可以强制推送的时机之一,是在将私有分支推送到远端之后执行了本地提纯的时候(比如为了备份)。这意味着“我不想push该分支的初始版本,使用现在这个代替吧”。再次强调的是,该分支的初始版本上,不应该再有其他人同步开发。
Workflow Walkthrough [工作流程]
Rebasing can be incorporated into your existing Git workflow as much or as little asyour team is comfortable with. In this section, we’ll take a look at thebenefits that rebasing can offer at the various stages of a feature’sdevelopment.
根据团队的接受程度,rebase可以被或多或少的引入既有的Git工作流中。在本章节中,将对不同开发场景下使用rebase的好处进行分析。
The first step in any workflow that leverages git rebase isto create a dedicated branch for each feature. This gives you the necessarybranch structure to safely utilize rebasing:
在任何对git rebase施加影响的工作流中做出的第一步,就是为每个特性创建一个专门的分支 – 这为rebase的安全运用提供了必要的目录结构。
Local Cleanup [本地提纯]
One of the best ways to incorporate rebasing into your workflow is to clean uplocal, in-progress features. By periodically performing an interactive rebase,you can make sure each commit in your feature is focused and meaningful. Thislets you write your code without worrying about breaking it up into isolatedcommits—you can fix it up after the fact.
向工作流中引入rebase的最好方式之一,是对未完成的特性进行本地提纯。通过定期执行交互式rebase,可以确保对特性的每次提交都是目标明确且有意义的。这样在编码时就用不着担心不同的commit会产生破坏—这可以在发生前就得到修正。
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 inthe Interactive Rebasing section. The latter option is nicewhen you only need to fix up the last few commits. For example, the followingcommand begins an interactive rebase of only the last 3 commits.
当调用gitrebase时,其实对于新的基点有两个选项:父分支(比如master),或者本分支的较早前的某次commit。前者在之前的“交互式rebase”章节中已经演示,后者则能很好的适用于只想修正前几个commit的情况。譬如,下面的命令对前三个commit开始了一次交互式rebase:
git checkout feature git rebase -i HEAD~3
By specifying HEAD~3 as the new base, you’re not actuallymoving the branch—you’re just interactively re-writing the 3 commits thatfollow it. Note that this will not incorporate upstreamchanges into the feature branch.
指定新基点为HEAD~3,其实并未实际移动分支 – 只是交互式的重写其了最近的3次commit(译注:编辑顺序等)。这并不会对分支引入逆向改变。
Ifyou want to re-write the entire feature using this method, the gitmerge-base commandcan be useful to find the original base of the feature branch.The following returns the commit ID of the original base, which you can thenpass to git rebase:
如果想用这种方法重写整个分支,则可用git merge-base命令找到分支的起始点。下面演示了如何找到起始点的commit ID,稍后可被用于传递给git rebase:
git merge-base feature master
This use of interactive rebasing is a great way to introduce gitrebase intoyour workflow, as it only affects local branches. The only thing otherdevelopers will see is your finished product, which should be a clean,easy-to-follow feature branch history.
如此,交互式rebase就以一种很好的方式被引入了工作流,并且只对本地分支产生影响。其他开发者只会看到你完成后的成果 – 一个干净且易易懂的分支历史。
But again, this only works for private feature branches. If you’recollaborating with other developers via the same feature branch, that branchis public, and you’re not allowed to re-write its history.
再次提示,只能在私有分支上这么干。如果你和其他开发者在一个分支上协同开发,就万万不能重写历史。
There is no git merge alternative for cleaning up localcommits with an interactive rebase.
没有git merge的替代方法可被用来提纯使用了交互式rebase的本地提交。
Incorporating Upstream Changes Into a Feature [向分支中引入逆向改变]
In the Conceptual Overview section, we saw how a feature branchcan incorporate upstream changes from master usingeither 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 thetip of master.
在“概念概览”章节,演示了用git merge 或git rebase向master引入逆向改变的例子。merge可以保护整个仓库的提交历史,因而很安全;而rebase则是通过移动分支到master开头端来创建一个线性的历史。
This use of git rebase is similar to a local cleanup (andcan be performed simultaneously), but in the process it incorporates thoseupstream commits from master.
git rebase的这种用法类似于本地提纯(也可以同时执行),但执行过程中,向master引入了逆向提交。
Keep in mind that it’s perfectly legal to rebase onto a remote branch insteadof master. This can happen when collaborating on thesame feature with another developer and you need to incorporate their changesinto your repository.
需要牢记的是,向一个非master的远端分支rebase是完全合法的。这通常出现在在某个分支上协同开发,而你需要向自己的仓库引入其他人的改变时。
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 branchfrom John’s repository:
举例来说,fetch远端目标分支后的情况如下:
You can resolve this fork the exact same way as you integrate upstream changesfrom master: either merge your local featurewith john/feature, orrebase your local feature onto the tip of john/feature.
可以采用从master整合逆向改变的相同方法来处理这个分岔:要么将本地更改和远端的更改merge,要么把本地更改rebase到远端分支的开头端。
Note that this rebase doesn’t violate the Golden Rule of Rebasingbecauseonly your local feature commits are being moved—everythingbefore that is untouched. This is like saying, “add my changes to what John hasalready done.” In most circumstances, this is more intuitive than synchronizingwith the remote branch via a merge commit.
这并未违反“rebase黄金法则”,因为只有本地分支的新提交被移动了,而之前发生的任何事情都为被改变。这相当于说:“把我的改变添加到已经完成那部分的后面去”,通常,这比merge后的并行更直观。
By default, the git pull command performs a merge, but you canforce it to integrate the remote branch with a rebase by passing it the --rebase option.
git pull会默认执行 merge,但可以通过 --rebase 选项强制使其通过rebase整合远端分支。
Reviewing a FeatureWith a Pull Request(略)
(译注:Bitbucket上方便开发者之间协作的功能,开发者向团队成员通知功能开发已经完成)
Ifyou use pull requests as part of your code review process, you need to avoidusing git rebase after creating the pull request. Assoon as you make the pull request, other developers will be looking at yourcommits, which means that it’s a public branch. Re-writing itshistory will make it impossible for Git and your teammates to track anyfollow-up commits added to the feature.
Anychanges from other developers need to be incorporated with gitmerge insteadof git rebase.
Forthis reason, it’s usually a good idea to clean up your code with an interactiverebase before submitting your pull request.
Integrating anApproved Feature [整合审核过的改变]
Aftera feature has been approved by your team, you have the option of rebasing thefeature onto the tip of the master branch before using gitmerge tointegrate the feature into the main code base.
当新的改变被团队认可后,可以选择在使用git merge整合改变到主代码之前,将改变rebase到master的开始端。
Thisis 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, byperforming a rebase before the merge, you’re assured that the merge will befast-forwarded, resulting in a perfectly linear history. This also gives youthe chance to squash any follow-up commits added during a pull request.
这和把逆向改变引入目标分支类似,但鉴于你不能重写master上的commit,你最终只能使用git merge整合改变。但是,在merge之前执行rebase的时候,必须确保merge是fast-forwarded的,可以形成完美的线性历史。
(译注:fast-forward简单来说就是提交到远程中心仓库的代码必须是按照时间顺序的,能够保证不会强制覆盖别人的代码)
If you’re not entirely comfortable with git rebase,you can always perform the rebase in a temporary branch. That way, if youaccidentally mess up your feature’s history, you can check out the originalbranch and try again. For example:
如果还不能完全适应git rebase,可以一直在一个临时分支中执行rebase。这样即使不小心搞乱了提交历史,也能checkout原始分支后再试一次。比如:
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 wouldprefer a clean, linear history free of unnecessary merge commits, you shouldreach for git rebaseinstead of git merge whenintegrating changes from another branch.
这些就是用rebase操作分支所要了解的了,如果比起来非必要的merge commit,更钟意干净、线性的提交历史,那就应该在整合改变到其他分支时试试用git rebase代替git merge。
Onthe other hand, if you want to preserve the complete history of your projectand avoid the risk of re-writing public commits, you can stick with gitmerge.Either option is perfectly valid, but at least now you have the option ofleveraging the benefits of git rebase.
换句话说,如果想要保存整个项目的完整提交历史,并避免重写公开commit的风险,那还是得坚持使用 git merge。哪种选项都很有效,但起码你现在有了更多的一种好的选择。