git rebase

rebase在git中是一个非常有魅力的命令,使用得当会极大提高自己的工作效率;相反,如果乱用,会给团队中其他人带来麻烦。它的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase命令可以使我们的提交历史干净、简洁!

写在前面:如果你的分支不只有你在进行开发,那么git -rebase可能会导致提交记录的丢失,但是如果你是在自己的分支上进行开发,那么这无疑是一个很棒的选择。

合并本地提交

一般情况下,我们进行开发时,都是从master分支拉一个自己的开发分支,进行代码修改操作,再git add以及git commit之后将我们修改好的代码git push到远程仓库。
但是,很多情况下,我们并不会仅仅在本地git commit一次,而是会执行很多次,而我们知道,每一个的git commit都会形成一个git节点,而如果我们把这些节点都push到远端,就会使项目的git日志很乱,因为你的这些commit对于其他同学来说都仅仅是为了完成你对应的修改工作,他们希望的是,你能在一次commit操作中把你的修改全部完成,这时,就可以用到我们的git rebase操作了,在git push之前,我们可以将几次本地的commit操作合并,这样,我们推送到远端的commit操作就只有一个了,更利于项目管理。

提交前

合并

命令

git rebase -i

pick f7f3f6d changed my name a bit 
pick 310154e updated README formatting and added blame 
pick a5f4a0d added cat-file

# Rebase f9423bf..f9423bf onto f9423bf (1 command)
#
# Commands:
# p, pick <commit> = use commit    // 正常提交
# r, reword <commit> = use commit, but edit the commit message    // 修改提交信息
# e, edit <commit> = use commit, but stop for amending    // 停下来修改一下
# s, squash <commit> = use commit, but meld into previous commit    // 融入之前的提交
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous    // 只保留最前边的commit信息

分支合并

首先通过简单的提交节点图解感受一下rebase在干什么
构造两个分支master和feature,其中feature是在提交点B处从master上拉出的分支
master上有一个新提交M,feature上有两个新提交C和D

此时我们切换到feature分支上,执行rebase命令,相当于是想要把master分支合并到feature分支(这一步的场景就可以类比为我们在自己的分支feature上开发了一段时间了,准备从主干master上拉一下最新改动。模拟了git pull --rebase的情形)

# 这两条命令等价于git rebase master feature
git checkout feature
git rebase master

下图为变基后的提交节点图,解释一下其工作原理:

* feature:待变基分支、当前分支
* master:基分支、目标分支

结合例子解释:当在feature分支上执行git rebase master时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,注意这里的接法,官方没说清楚,实际是会依次拿M和C、D内容分别比较,处理冲突后生成新的C’和D’。一定注意,这里新C’、D’和之前的C、D已经不一样了,是我们处理冲突后的新内容,feature指针自然最后也是指向D’

通俗解释(重要!!):rebase,变基,可以直接理解为改变基底。feature分支是基于master分支的B拉出来的分支,feature的基底是B。而master在B之后有新的提交,就相当于此时要用master上新的提交来作为feature分支的新基底。实际操作为把B之后feature的提交先暂存下来,然后删掉原来这些提交,再找到master的最新提交位置,把存下来的提交再接上去(接上去是逐个和新基底处理冲突的过程),如此feature分支的基底就相当于变成了M而不是原来的B了。(注意,如果master上在B以后没有新提交,那么就还是用原来的B作为基,rebase操作相当于无效,此时和git merge就基本没区别了,差异只在于git merge会多一条记录Merge操作的提交记录)

上面的例子可抽象为如下实际工作场景:张三从B拉了代码进行开发,目前提交了两次,开发到D了;李四也从B拉出来开发了并且开发完毕,他提交到了M,然后合到主干上了。此时张三想拉下最新代码,于是他在feature分支上执行了git rebase master,即把master分支给rebase过来,由于李四更早开发完并合了主干,如此就相当于张三是基于李四的最新提交M进行的开发了。

此时,在feature分支上执行git rebase master后,会提示有冲突,这里是关键,之前没有把这个细节说清楚。冲突其实也简单,因为我们要生成新的C’和D’嘛,那C’的内容如何得到呢?照搬C的?当然不是,C’的内容就是C和M两个节点的内容合并的结果,D’的内容就是D和M两个节点的内容合并的结果。我们手动处理冲突后,执行如下命令即可:

# 先处理完C,会继续报D的冲突,所以下面命令一共会执行两次 
git add file
git rebase --continue
# 从头开始
git rebase --abort

变基前

变基后

推荐使用场景

搞来搞去那么多,这其实是最重要的。不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:

自己单机的时候,拉公共分支最新代码的时候使用rebase,也就是git pull -rgit pull --rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。总体来说,即使是单机也不建议使用,确实有点绕啊QAQ

往公共分支上合代码的时候,使用merge。如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行 git rebase 李四的开发分支 ,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了。

正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇