Merge pull request #1772 from geekpi/master

[Translated] Git Rebase Tutorial: Going Back in Time with Git Rebase
This commit is contained in:
geekpi 2014-10-04 11:37:50 +08:00
commit 64d3f9b525
2 changed files with 109 additions and 110 deletions

View File

@ -1,110 +0,0 @@
Translating--------geekpi
Git Rebase Tutorial: Going Back in Time with Git Rebase
================================================================================
![](https://www.gravatar.com/avatar/7c148ace0d63306091cc79ed9d9e77b4?d=mm&s=200)
A programmer since the tender age of 10, Christoph Burgdorf is the the founder of the HannoverJS meetup, and he has been an active member in the AngularJS community since its very beginning. He is also very knowledgeable about the ins and outs of git, where he hosts workshops at [thoughtram][1] to help beginners master the technology.
The following tutorial was originally posted on his [blog][2].
----------
### Tutorial: Git Rebase ###
Imagine you are working on that radical new feature. Its going to be brilliant but it takes a while. Youve been working on that for a couple of days now, maybe weeks.
Your feature branch is already six commits ahead of master. Youve been a good developer and have crafted meaningful semantical commits. But theres the thing: you are slowly realizing that this beast will still take some more time before its really ready to be merged back into master.
m1-m2-m3-m4 (master)
\
f1-f2-f3-f4-f5-f6(feature)
What you also realize is that some parts are actually less coupled to the new feature. They could land in master earlier. Unfortunately, the part that you want to port back into master earlier is in a commit somewhere in the middle of your six commits. Even worse, it also contains a change that relies on a previous commits of your feature branch. One could argue that you should have made that two commits in the first place, but then nobody is perfect.
m1-m2-m3-m4 (master)
\
f1-f2-f3-f4-f5-f6(feature)
^
|
mixed commit
At the time that you crafted the commit, you didnt foresee that you might come into a situation where you want to gradually bring the feature into master. Heck! You wouldnt have guessed that this whole thing could take us so long.
What you need is a way to go back in history, open up the commit and split it into two commits so that you can separate out all the things that are safe to be ported back into master by now.
Speaking in terms of a graph, we want to have it like this.
m1-m2-m3-m4 (master)
\
f1-f2-f3a-f3b-f4-f5-f6(feature)
With the work split into two commits, we could just cherry-pick the precious bits into master.
Turns out, git comes with a powerful command git rebase -i which lets us do exactly that. It lets us change the history. Changing the history can be problematic and as a rule of thumb should be avoided as soon as the history is shared with others. In our case though, we are just changing history of our local feature branch. Nobody will get hurt. Promised!
Ok, lets take a closer look at what exactly happened in commit f3. Turns out we modified two files: userService.js and wishlistService.js. Lets say that the changes to userService.js could go straight back into master whereas the changes to wishlistService.js could not. Because wishlistService.js does not even exist in master. It was introduced in commit f1.
> Pro Tip: even if the changes would have been in one file, git could handle that. We keep things simple for this blog post though.
Weve set up a [public demo repository][3] that we will use for this exercise. To make it easier to follow, each commit message is prefixed with the pseudo SHAs used in the graphs above. What follows is the branch graph as printed by git before we start to split the commit f3.
![](https://s3.amazonaws.com/codementor_content/2014-Sep-week3/git1.png)
Now the first thing we want to do is to checkout our feature branch with git checkout feature. To get started with the rebase we run git rebase -i master.
Now what follows is that git opens a temporary file in the configured editor (defaults to Vim).
![](https://s3.amazonaws.com/codementor_content/2014-Sep-week3/git2.png)
This file is meant to provide you some options for the rebase and it comes with a little cheat sheet (the blue text). For each commit we could choose between the actions pick, reword, edit, squash, fixup and exec. Each action can also be referred to by its short form p, r, e, s, f and e. Its out of the scope of this article to describe each and every option so lets focus on our specific task.
We want to choose the edit option for our f3 commit hence we change the contents to look like that.
Now we save the file (in Vim <ESC> followed by :wq, followed by <RETURN>). The next thing we notice is that git stops the rebase at the commit for which we choose the edit option.
What this means is that git started to apply f1, f2 and f3 as if it was a regular rebase but then stopped **after** applying f3. In fact, we can prove that if we just look at the log at the point where we stopped.
To split our commit f3 into two commits, all we have to do at this point is to reset gits pointer to the previous commit (f2) while keeping the working directory the same as it is right now. This is exactly what the mixed mode of git reset does. Since mixed is the default mode of git reset we can just write git reset head~1. Lets do that and also run git status right after it to see what happened.
The git status tells us that both our userService.js and our wishlistService.js are modified. If we run git diff we can see that those are exactly the changes of our f3 commit.
If we look at the log again at this point we see that the f3 is gone though.
We are now at the point that we have the changes of our previous f3 commit ready to be committed whereas the original f3 commit itself is gone. Keep in mind though that we are still in the middle of a rebase. Our f4, f5 and f6 commits are not lost, theyll be back in a moment.
Lets make two new commits: Lets start with the commit for the changes made to the userService.js which are fine to get picked into master. Run git add userService.js followed by git commit -m "f3a: add updateUser method".
Great! Lets create another commit for the changes made to wishlistService.js. Run git add wishlistService.js followed by git commit -m "f3b: add addItems method".
Lets take a look at the log again.
This is exactly what we wanted except our commits f4, f5 and f6 are still missing. This is because we are still in the middle of the interactive rebase and we need to tell git to continue with the rebase. This is done with the command git rebase --continue.
Lets check out the log again.
And thats it. We now have the history we wanted. The previous f3 commit is now split into two commits f3a and f3b. The only thing left to do is to cherry-pick the f3a commit over to the master branch.
To finish the last step we first switch to the master branch. We do this with git checkout master. Now we can pick the f3a commit with the cherry-pick command. We can refer to the commit by its SHA key which is bd47ee1 in this case.
We now have the f3a commit sitting on top of latest master. Exactly what we wanted!
Given the length of the post this may seem like a lot of effort but its really only a matter of seconds for an advanced git user.
> Note: Christoph is currently writing a book on [rebasing with Git][4] together with Pascal Precht, and you can subscribe to it at leanpub to get notified when its ready.
--------------------------------------------------------------------------------
via: https://www.codementor.io/git-tutorial/git-rebase-split-old-commit-master
作者:[cburgdorf][a]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出
[a]:https://www.codementor.io/cburgdorf
[1]:http://thoughtram.io/
[2]:http://blog.thoughtram.io/posts/going-back-in-time-to-split-older-commits/
[3]:https://github.com/thoughtram/interactive-rebase-demo
[4]:https://leanpub.com/rebase-the-complete-guide-on-rebasing-in-git

View File

@ -0,0 +1,109 @@
Git Rebase教程 用Git Rebase让时光倒流
================================================================================
![](https://www.gravatar.com/avatar/7c148ace0d63306091cc79ed9d9e77b4?d=mm&s=200)
Christoph Burgdorf自10岁时就是一名程序员他是HannoverJS Meetup网站的创始人并且一直活跃在AngularJS社区。他也是非常了解gti的内内外外在那里他举办一个[thoughtram][1]的工作室来帮助初学者掌握该技术。
下面的教程最初发表在他的[blog][2]。
----------
### 教程: Git Rebase ###
想象一下你正在开发一个激进的新功能。这将是很灿烂的但它需要一段时间。您这几天也许是几个星期一直在做这个。
你的功能分支已经超前master有6个提交了。你是一个优秀的开发人员并做了有意义的语义提交。但有一件事情你开始慢慢意识到这个野兽仍需要更多的时间才能真的做好准备被合并回主分支。
m1-m2-m3-m4 (master)
\
f1-f2-f3-f4-f5-f6(feature)
你也知道的是,一些地方实际上是少耦合的新功能。它们可以更早地合并到主分支。不幸的是,你想将部分合并到主分支的内容存在于你六个提交中的某个地方。更糟糕的是,它也包含了依赖于你的功能分支的之前的提交。有人可能会说,你应该在第一处地方做两次提交,但没有人是完美的。
m1-m2-m3-m4 (master)
\
f1-f2-f3-f4-f5-f6(feature)
^
|
mixed commit
在你准备提交的时间,你没有预见到,你可能要逐步把该功能合并入主分支。哎呀!你不会想到这件事会有这么久。
你需要的是一种方法可以回溯历史把它并分成两次提交这样就可以把代码都安全地分离出来并可以移植到master分支。
用图说话,就是我们需要这样。
m1-m2-m3-m4 (master)
\
f1-f2-f3a-f3b-f4-f5-f6(feature)
在将工作分成两个提交后我们就可以cherry-pick出前面的部分到主分支了。
原来Git自带了一个功能强大的命令git rebase -i ,它可以让我们这样做。它可以让我们改变历史。改变历史可能会产生问题,并作为一个经验法应尽快避免历史与他人共享。在我们的例子中,虽然我们只是改变我们的本地功能分支的历史。没有人会受到伤害。这这么做了!
好吧让我们来仔细看看f3提交究竟修改了什么。原来我们共修改了两个文件userService.js和wishlistService.js。比方说userService.js的更改可以直接合入主分支而wishlistService.js不能。因为wishlistService.js甚至没有在主分支存在。这根据的是f1提交中的介绍。
>>专家提示即使是在一个文件中更改git也可以搞定。但这篇博客中我们要让事情变得简单。
我们已经建立了一个[公众演示仓库][3]我们将使用这个来练习。为了便于跟踪每一个提交信息的前缀是在上面的图表中使用的假的SHA。以下是git在分开提交f3时的分支图。
![](https://s3.amazonaws.com/codementor_content/2014-Sep-week3/git1.png)
现在我们要做的第一件事就是使用git的checkout功能checkout出我们的功能分支。用git rebase -i master开始做rebase。
现在接下来git会用配置的编辑器打开默认为Vim一个临时文件。
![](https://s3.amazonaws.com/codementor_content/2014-Sep-week3/git2.png)
该文件为您提供一些rebase选择它带有一个提示蓝色文字。对于每一个提交我们可以选择的动作有pick、rwork、edit、squash、fixup和exec。每一个动作也可以通过它的缩写形式p、r、e、s、f和e引用。描述每一个选项超出了本文范畴所以让我们专注于我们的具体任务。
我们要为f3提交选择编辑选项因此我们把内容改变成这样。
现在我们保存文件在Vim中是按下<ESC>后输入:wq,最后是按下回车。接下来我们注意到git在编辑选项中选择的提交处停止了rebase。
这意味这git开始为f1、f2、f3生效仿佛它就是常规的rebase但是在f3**之后**停止。事实上,我们可以看一眼停止的地方的日志就可以证明这一点。
要将f3分成两个提交我们所要做的是重置git的指针到先前的提交f2而保持工作目录和现在一样。这就是git reset在混合模式在做的。由于混合模式是git reset的默认模式我们可以直接用git reset head~1。就这么做并在运行后用git status看下发生了什么。
git status告诉我们userService.js和wishlistService.js被修改了。如果我们与行git diff 我们就可以看见在f3里面确切地做了哪些更改。
如果我们看一眼日志我们会发现f3已经消失了。
现在我们有了准备提交的先前的f3提交而原先的f3提交已经消失了。记住虽然我们仍旧在rebase的中间过程。我们的f4、f5、f6提交还没有缺失它们会在接下来回来。
让我们创建两个新的提交首先让我们为可以提交到主分支的userService.js创建一个提交。运行git add userService.js 接着运行 git commit -m "f3a: add updateUser method"。
太棒了让我们为wishlistService.js的改变创建另外一个提交。运行git add wishlistService.js接着运行git commit -m "f3b: add addItems method".
让我们在看一眼日志。
这就是我们想要的除了f4、f5、f6仍旧缺失。这是因为我们仍在rebase交互的中间我们需要告诉git继续rebase。用下面的命令继续git rebase --continue。
让我们再次检查一下日志。
就是这样。我们现在已经得到我们想要的历史了。先前的f3提交现在已经被分割成两个提交f3a和f3b。剩下的最后一件事是cherry-pick出f3a提交到主分支上。
为了完成最后一步我们首先切换到主分支。我们用git checkout master。现在我们就可以用cherry-pick命令来拾取f3a commit了。本例中我们可以用它的SHA值bd47ee1来引用它。
现在f3a这个提交i就在主分支的最上面了。这就是我们需要的
这篇文章的长度看起来需要花费很大的功夫但实际上对于一个git高级用户而言这只是一会会。
>注Christoph目前正在与Pascal Precht写一本关于[Git rebase][4]的书您可以在leanpub订阅它并在准备出版时获得通知。
--------------------------------------------------------------------------------
via: https://www.codementor.io/git-tutorial/git-rebase-split-old-commit-master
作者:[cburgdorf][a]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出
[a]:https://www.codementor.io/cburgdorf
[1]:http://thoughtram.io/
[2]:http://blog.thoughtram.io/posts/going-back-in-time-to-split-older-commits/
[3]:https://github.com/thoughtram/interactive-rebase-demo
[4]:https://leanpub.com/rebase-the-complete-guide-on-rebasing-in-git