博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Git] Git整理(四) git rebase 的使用
阅读量:7226 次
发布时间:2019-06-29

本文共 8712 字,大约阅读时间需要 29 分钟。

hot3.png

概述

在之前总结分支相关内容时说道,合并两个分支的提交可以使用git merge,然而除了这种方式之外,还有一种方式就是使用git rebase,这两种方式的最终结果都相同,但是合并历史却不同;git merge是将两个分支做一个三方合并(如果不是直接上游分支),这样一来,查看提交历史记录,可能会显得非常凌乱。git rebase则会将当前分支相对于基低分支的所有提交生成一系列补丁,然后放到基底分支的顶端,从而使得提交记录变称一条直线,非常整洁。

git merge 和 git rebase 区别

假设现在本地仓库中有两个分支:master分支和branch1分支,提交历史用图来表示如下:

现在要合并branch1到master分支,如果使用git merge则执行如下命令:

$ git checkout master

$ git merge branch1
合并后查看提交历史如下:

$ git log --graph --pretty="oneline"

*   fe8799e0aec30e388306883960b4cf438d3f1ec4 Merge branch 'branch1'
|\  
| * cf31255da6e84acc6f6840e3ceb0fd3129e2d73e UserA commit 3--branch1
| * 5c2d1c938f8e5f98dccaa0a5ab6222bd6b1cd75d UserA commit 2--branch1
* | 284aa3eb6c405411584d682a1387118fe92e4821 Usera commit master
* | 967fca58deb914ad1cda9ff84291fd946045207d Usera commit master
|/  
* d989fc50530918b3b7b0ed68b31d6751c2302875 UserA commit 1
使用图来表示,本地仓库提交历史如下:

现在我们使用git rebase合并原来的master分支和branch1分支,假设当前分支为branch1,基地分支为master:

$ git checkout branch1

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: UserA commit 2--branch1
Applying: UserA commit 3--branch1
Applying: Usera commit master
Applying: Usera commit master
合并后查看提交历史如下:

$ git log --graph --pretty="oneline"

* 6cf95c391ba7d43d0f5d95300130a43816af82c8 Usera commit master
* 63def8a8740b9b3c9f6c09ae49ba72faa9446cf6 Usera commit master
* 33049864f83a686bff9b2a2d8626427653a16f22 UserA commit 3--branch1
* 14ac1cac7357ccf35581c89e099793260264d3ea UserA commit 2--branch1
* d989fc50530918b3b7b0ed68b31d6751c2302875 UserA commit 1
使用图来表示,本地仓库提交历史如下:

可以看到,现在branch1分支上相对于master分支的提交,提交到了master分支的顶端,如此一来整个提交记录保持在一条直线上。这就是git rebase。

rebase原理

git rebase <branch>的原理是:找到两个分支最近的共同祖先,根据当前分支(上例中branch1)的提交历史生成一系列补丁文件,然后以基地分支最后一个提交为新的提交起始点,应用之前生成的补丁文件,最后形成一个新的合并提交。从而使得变基分支成为基地分支的直接下游。rebase一般被翻译为变基。

当branch1分支完成变基后,直接变成了master分支的下游了,这时切换到master分支,直接通过git merge即可将branch1分支的合并到master分支上:

$ git merge branch1 

Updating ff7658d..d6168dc
Fast-forward
 test.txt | 1 +
 1 file changed, 1 insertion(+)
掌握了rebase基本原理后,接下来对该命令一些参数进行下总结。

git rebase branchA branchB

首先会取出branchB,将branchB中的提交放在branchA的顶端,一般branchB为当前分支,可以不指定。

假设当前本地仓库提交历史如下,且处于topic分支:

     A---B---C topic

    /
D---E---F---G master
此时我们使用git rebase将两个分支的提交合并到master分支的顶端:

$ git rebase master

# 或者
$ git rebase master topic
此时,提交历史将变为:

             A'--B'--C' topic

            /
D---E---F---G master
git rebase --onto
如果要在合并两分支时需要跳过某一分支的提交,这时就可以使用--onto参数了。比如,假设当前本地仓库提交历史如下:

A---B---E---F---G  master

    \
     C---D---H---I---J  next
                      \
                       K---L---M  topic
此时topic分支的上游分支是next分支,如果要将topic分支上的提交(K,M,L)跳过next分支,直接放到master分支上,就需要加上--onto参数:

$ git rebase --onto master next topic

1
上述命令的意思是:取出topic分支,找出topic和next分支的共同祖先之后的提交,然后放在master分支上,执行后提交历史变为如下:

A---B---E---F---G  master

    \            \
     \            K'---L'---M'  topic 
      \     
       C---D---H---I---J  next
                      
git rebase --continue/abort/skip
这三个命令分别表示:继续执行变基操作、终止变基、跳过某一文件继续进行。在rebase的过程中,有可能会出现文件冲突,比如:

$ git rebase master

First, rewinding head to replay your work on top of it...
Applying: [Description]:test1
Using index info to reconstruct a base tree...
M    test.txt
Falling back to patching base and 3-way merge...
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
error: Failed to merge in the changes.
Patch failed at 0001 [Description]:test1
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".

If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
这种情况下,首先要解决冲突,解决冲突后可以选择继续执行rebase或者结束rebase,一般的做法为:

$ git add filename

$ git rebase --continue
1
2
或者可以选择终止变基:

$ git rebase --abort

1
或者跳过该patch:

$ git rebase --skip

1
git rebase -i
该命令相比其他命令,使用频率要高得多。git rebase -i <commitid>可以进行交互式变基,相比于git rebase <branch>用来变基,它经常用来操作当前分支的提交,git会将<commitid>-HEAD之间的提交列在一个变基脚本中,每一笔提交根据用户设置的命令,会进行不同的操作,如修改提交信息、移除指定提交、合并两个提交为一个(压缩提交)、拆分提交等。

如,要对最近4次提交进行重新操作,则:

$ git rebase -i HEAD~4

1
此时将会弹出如下形式的变基脚本:

  1 pick af98479 [Description]:test4

  2 pick 3cc9d66 test3
  3 pick a7e819e usera commit03 branch2
  4 pick efc5b15 usera commit04 branch2
  5 
  6 # Rebase de7b118..efc5b15 onto de7b118 (4 command(s))
  7 #
  8 # Commands:
  9 # p, pick = use commit
 10 # r, reword = use commit, but edit the commit message
 11 # e, edit = use commit, but stop for amending
 12 # s, squash = use commit, but meld into previous commit
 13 # f, fixup = like "squash", but discard this commit's log message
 14 # x, exec = run command (the rest of the line) using shell
 15 # d, drop = remove commit
 16 #
 17 # These lines can be re-ordered; they are executed from top to bottom.
 18 #
 19 # If you remove a line here THAT COMMIT WILL BE LOST.
 20 #
 21 # However, if you remove everything, the rebase will be aborted.
 22 #
 23 # Note that empty commits are commented out

这里我们可以修改pick为下面给出的其他命令,比如如果要修改提交信息,就使用r或reword,各指令的含义如下:

p,pick:直接使用该次提交

r,reword:使用该次提交,但重新编辑提交信息
e,edit:停止到该次提交,通过`git commit --amend`追加提交,完毕之后不要忘记使用`git rebase --continue`完成这此rebase
s,squash,压缩提交,将和上一次提交合并为一个提交
x,exec,运行命令
d,drop,移除这次提交
接下来我们看看他们如何使用。

1.修改多个提交信息

使用git commit --amend在最近一次提交上追加提交,因此可以使用该命令来修改最后一次的提交信息,如果要修改做个提交信息,需要git rebase -i <commitid>打开变基脚本后在需要修改信息的提交上执行reword操作,比如以下示例:

查看最近四次提交记录

$ git log --oneline -4

d0a80c2 02-b2
b6c6595 01-b2
ea2f366 b2
49afab9 commit branch3w
提交信息非常不人性化,因此对以上几个提交记录修改提交信息,将默认的pick改为r或者reword:

$ git rebase -i HEAD~4

  1 r 49afab9 commit branch3w

  2 r ea2f366 b2
  3 r b6c6595 01-b2
  4 r d0a80c2 02-b2
  5 # ......
保存退出后,会弹出编译器输入提交信息,输入完毕后:

$ git rebase -i HEAD~4

[detached HEAD afeaed3] commit first
 Date: Wed Jun 27 20:26:33 2018 +0800
 1 file changed, 1 insertion(+), 1 deletion(-)
[detached HEAD 528910c] commit second
 Date: Wed Jun 27 20:29:07 2018 +0800
 1 file changed, 1 deletion(-)
[detached HEAD 0e09a0f] commit 3th
 Date: Wed Jun 27 20:29:25 2018 +0800
 1 file changed, 1 deletion(-)
[detached HEAD eaed13d] commit 4th
 Date: Wed Jun 27 20:29:35 2018 +0800
 1 file changed, 1 deletion(-)
Successfully rebased and updated detached HEAD.

再次查看提交log:

$ git log --oneline -4

eaed13d commit 4th
0e09a0f commit 3th
528910c commit second
afeaed3 commit first
利用git rebase -i <commitid>和reword就可以完成修改多次提交信息了。

2.重新排序提交

改变变基脚本中的顺序就可以对之前的提交重新排序,如:
选择最近4次提交进行处理:

$ git rebase -i HEAD~4

此时会打开变基脚本,在脚本中将second这次提交放在最后一次提交中:

  1 pick ecd66f5 commit first

  2 pick 7dbfe25 commit 3th
  3 pick 82ba6a6 commit 4th
  4 pick a77e06e commit second
  5 # .....
保存退出,查看提交log,发现second变为最后一次提交:

$ git log --oneline -4

fe15bdb commit second
18fa9a9 commit 4th
d08c408 commit 3th
ecd66f5 commit first
3.压缩提交
如果要压缩两个提交为一次,需要git rebase -i <commitid>打开变基脚本后在需要压缩的提交上执行squash操作,当保存退出后,会将该笔提交和上一笔提交压缩为一个提交,比如:

先查看当前提交记录:

$ git log --oneline -4

fe15bdb commit second
18fa9a9 commit 4th
d08c408 commit 3th
ecd66f5 commit first
现在我们将4th和3th这两笔提交压缩为一笔提交,在执行git rebase -i HEAD~4后在变基脚本中做如下修改:

  1 pick ecd66f5 commit first

  2 pick d08c408 commit 3th
  3 squash 18fa9a9 commit 4th
  4 pick fe15bdb commit second
保存退出后,输入一下提交信息,成功后再查看log:

$ git log --oneline -4

cf4159b commit second
9d73407 commit----compress 3th and 4th
ecd66f5 commit first
说明合并成功了,如果要对多个提交进行合并压缩,则可以按照如下的方式:

  1 pick ecd66f5 commit first

  2 squash d08c408 commit 3th
  3 squash 18fa9a9 commit 4th
  4 pick fe15bdb commit second
这表示会将first、3th、4th进行合并。

4.拆分提交

如果要将一次提交拆分为多次提交,则可以将变基脚本中对应提交的指令修改为edit。拆分一个提交会撤消这个提交,然后多次地、部分地、暂存与提交直到完成你所需次数的提交。比如下面示例:

首先查看提交记录:

$ git log --oneline -4

2a5c2aa commit 4th
f2ceb0f commit 3th
20fe2f9 commit second
c51adbe commit first
现在修改3th这次提交,将这次提交拆分为多次提交,首先执行git rebase -i HEAD~3,然后在变基脚本中将该次提交指令改为edit:

  1 pick 20fe2f9 commit second

  2 edit f2ceb0f commit 3th
  3 pick 2a5c2aa commit 4th
  # ......
保存退出后,再次查看提交记录:

$ git log --oneline -3

f2ceb0f commit 3th
20fe2f9 commit second
c51adbe commit first
也就是说,3th这次提交是现在最近的一次提交了,我们要拆分这次提交,那就就要重置这次提交,让HEAD指针指向上一次提交:

$ git reset HEAD~

现在进行多次提交:

$ git add .

$ git commit -m "split commit3th---1"
$ git add .
$ git commit -m "split commit3th---2"
......
$ git add .
$ git commit -m "split commit3th---n"
满足自己拆分后,继续完成这次rebase:

$ git rebase --continue

最后查看提交记录,原来的提交被移除,新增了三条:

$ git log --oneline -6

1df4a4d split 3th----3
1c22d70 split 3th----2
dbc7d91 split 3th----1
20fe2f9 commit second
c51adbe commit first
5.移除提交
如果要移除某次提交,可以在变基脚本中将对应提交指令改为drop,或者直接干脆删除,比如要移除上例中新家的三个记录:

  1 pick c51adbe commit first

  2 pick 20fe2f9 commit second
  3 drop dbc7d91 split 3th----1
  4 drop 1c22d70 split 3th----2
  5 #pick 1df4a4d split 3th----3
查看提交记录:

$ git log --oneline -6

20fe2f9 commit second
c51adbe commit first
rebase的风险
一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行变基操作。
因为不管是git rebase <branch>还是git rebase <commitid>,都重置了提交的SHA-1校验和,当你将本地变基后的提交推送到远端后,别人从服务器同步代码时,由于相同的内容却有不同的SHA-1校验值,因此会再此进行一次合并,于是就会有两个作者、commit信息完全相同的提交,但是SHA-1校验值不同,这无疑会带来麻烦。
因此,如果把变基当成一种在推送之前清理提交历史的手段,而且仅仅变基那些尚未公开的提交对象,就没问题。如果变基那些已经公开的提交对象,并且已经有人基于这些提交对象开展了后续开发工作的话,就会出现叫人沮丧的麻烦。

转载于:https://my.oschina.net/u/920274/blog/3024879

你可能感兴趣的文章
数据库SQL归纳(一)
查看>>
第9条:覆盖equals时总要覆盖hashCode
查看>>
产品经理工具
查看>>
ny495 少年 DXH
查看>>
http://www.cnblogs.com/doubleliang/tag/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/
查看>>
关于树及其各种操作
查看>>
医生问题
查看>>
Sublime Text3 配置 NodeJs 开发环境
查看>>
javascript中的对象查找
查看>>
给年轻工程师的10大忠告
查看>>
补习系列(7)-springboot 实现拦截的五种姿势
查看>>
Github网站加载不完全,响应超时,如何解决
查看>>
rem自适应布局
查看>>
django 坑~~
查看>>
Python 函数
查看>>
Linux64位程序中的漏洞利用
查看>>
Shell 编程(实例二)
查看>>
dp --- 二维dp + 最大上升子序列
查看>>
Android - TabHost 选项卡功能用法详解
查看>>
数据结构03-栈和队列
查看>>