git rebase

rebase 这个命令正式工作中基本上没有用过,只是学习时曾经写过 Demo,但具体指令的含义不是太理解,总觉得没有 merge 来得有掌控感,而且过去使用代码出过问题,所以一直知道但没去用它。

比如平时在 develop 分支上开发,因为想做什么试验临时开了个 feature-test 分支,如果没有切到 develop 再继续做事,那么 merge 也就是一个快进,没什么影响,如果 develop 又提交,再 merge 那么在提交历史上有 merge feature-test 之类的记录。虽然先 merge 后也可以通过交互式的重写历史,但那样更麻烦。而 rebase 本就是为了解决这种情况而存在的,所以还是再去看看。大概读的次数多了,这次再看感觉一下子豁然开朗明白了,如同当年初读《错误》这首诗觉得什么东西,连个韵都不搭,一点美感都无,但读的次数多了,某一次你顿悟了,只觉得如此婉约如此之美。

记得过去看《Pro Git》中文译名叫“衍合”的,等到了第二版中文译名变成“变基”了,其实“衍合”叫惯了,索性不说中文名了,直接叫 rebase 吧。

git rebase [目标分支]

假设有两个分支:dev 和 test,并且当前处于 test 分支,执行 git rebase dev 就是找到这两者共同的父提交 A,把 test 在这之后的操作(C、D)在 dev 上重做,这样两个 test 分支分叉开来的链路就没了,rebase 后产生两个新提交 C' 和 D',C' 的父提交是 B,test 指向 D'。dev 分支指针不变,仍然指向 B。

最后 HEAD 指针仍然指向 test,要更新 dev,需要切换然后 merge 快进。

创建文件夹,新建 test.txt 测试。

$ git init
$ git add test.txt
$ git commit -m 'master commit' # 相当于 A
$ git checkout -b dev
复制代码

修改文本内容为:

branch dev update
复制代码
$ git commit -a -m 'dev commit' # 相当于 B
$ git checkout master
$ git checkout -b test
复制代码

连续修改两次文本,并提交两次,结果文本内容为:

branch test update 1
branch test update 2
复制代码
$ git log --pretty=oneline
f5a18e72c93108a29a59993380d76d02f8819439 (HEAD -> test) test commit 2   # 相当于 D
422c92b2c4869927ed9bb11a6d35b903480d7f0b test commit 1  # 相当于 C
1835294189dc0724dc4fff8a9eb2963da1411810 (master) master commit   # 相当于 A

$ git branch
  dev
  master
* test  # 当前在 test 分支

$ git rebase dev
First, rewinding head to replay your work on top of it...
Applying: test commit 1
error: Failed to merge in the changes.
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
Patch failed at 0001 test commit 1
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
复制代码

有冲突,按提示解决,打开文本:

<<<<<<< HEAD
branch dev update
=======
branch test update 1
>>>>>>> test commit 1
复制代码

修改成:

branch dev update
branch test update 1
复制代码
$ git add test.txt
$ git rebase --continue
Applying: test commit 1
Applying: test commit 2
error: Failed to merge in the changes.
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
Patch failed at 0002 test commit 2
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
复制代码

又冲突,现在文本为:

<<<<<<< HEAD
branch dev update
branch test update 1
=======
branch test update 1
branch test update 2
>>>>>>> test commit 2
复制代码

两个分支文本第一行,第一次冲突解决,由于 test 分支第二次提交时第一行还是原来的,所以又冲突,所以如果要被 rebase 的分支如果有多个提交历史,需要多次 rebase,可能在冲突时出现重复的内容,也不敢在第一次冲突就只保留 dev 的内容,这样代码很多时得小心。修改文本为:

branch dev update
branch test update 1
branch test update 2
复制代码
$ git add test.txt
$ git rebase --continue
Applying: test commit 2

$ git log --graph
* commit f7971b5e7fa18039bcef610496fe202ce16f43f0 (HEAD -> test)
| Author: mazhijun <zhijun.ma@chelaile.net.cn>
| Date:   Thu Mar 8 13:09:32 2018 +0800
|
|     test commit 2
|
* commit 0df6161052eb881e5d89b2ea1afce6194b47884e
| Author: mazhijun <zhijun.ma@chelaile.net.cn>
| Date:   Thu Mar 8 13:09:10 2018 +0800
|
|     test commit 1
|
* commit df715abd4342208f238ff4e58c559c0e7ebb352c (dev)
| Author: mazhijun <zhijun.ma@chelaile.net.cn>
| Date:   Thu Mar 8 12:16:56 2018 +0800
|
|     dev commit
|
* commit 1835294189dc0724dc4fff8a9eb2963da1411810 (master)
  Author: mazhijun <zhijun.ma@chelaile.net.cn>
  Date:   Thu Mar 8 12:13:58 2018 +0800

      master commit
复制代码

对比原来的 log,可见 test 分支过去的两次提交历史没了,出现了两次新的提交,并且 HEAD -> test

$ git checkout dev

$ git merge test
Updating df715ab..f7971b5
Fast-forward        # 直接快进
 test.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

$ git log --pretty=oneline
f7971b5e7fa18039bcef610496fe202ce16f43f0 (HEAD -> dev, test) test commit 2  
0df6161052eb881e5d89b2ea1afce6194b47884e test commit 1
df715abd4342208f238ff4e58c559c0e7ebb352c dev commit
1835294189dc0724dc4fff8a9eb2963da1411810 (master) master commit
复制代码

HEAD -> dev, test,因为它们现在在一个地方,同时指着 D'。

git rebase [目标分支] [被 rebase 分支]

比如 git rebase dev test 就是将 test 分支 rebase 到 dev 里,和上面指令的区别是:执行这条指令不需要当前处于 test 分支。

先通过 reset 将 dev 分支指针移到 B 上面,将 test 分支指针移到 A 上面,并且保留代码:

$ git reset --hard HEAD~2
$ git checkout test
$ git reset --soft HEAD~3
复制代码

然后提交,这样 dev 和 test 有个分叉了:

$ git commit -m 'test commit'
复制代码

文本内容是:

branch dev update
branch test update 1
branch test update 2
复制代码
$ git checkout dev    # 当前不在 test 分支
$ git rebase dev test
First, rewinding head to replay your work on top of it...
Applying: test commit
error: Failed to merge in the changes.
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
Patch failed at 0001 test commit
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
复制代码

文本为:

<<<<<<< HEAD
branch dev update
=======
branch dev update
branch test update 1
branch test update 2
>>>>>>> test commit
复制代码

修改为

branch dev update
branch test update 1
branch test update 2
复制代码
$ git add test.txt
$ git rebase --continue
Applying: test commit
复制代码

在 dev 分支执行这个指令,rebase 之后自动切换到了 test 分支,并且有了一个新提交 E'。

$ git branch
  dev
  master
* test

$ git log --pretty=oneline
c57c2d1f49f0f97b5ed4a3145fba786d7bdafec4 (HEAD -> test) test commit
df715abd4342208f238ff4e58c559c0e7ebb352c (dev) dev commit
1835294189dc0724dc4fff8a9eb2963da1411810 (master) master commit
复制代码

如果不是在 dev 分支执行指令,而是在一个完全不相干的分支执行会怎么样?

$ git reset --soft HEAD~2 # test 指针到了 A
$ git commit -m 'test commit'
$ git checkout master
$ git checkout -b feature
复制代码

修改文本内容:

branch feature update
复制代码
$ git commit -a -m 'feature commit'
复制代码

现在的状态是:

$ git rebase dev test
First, rewinding head to replay your work on top of it...
Applying: test commit
error: Failed to merge in the changes.
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
Patch failed at 0001 test commit
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
复制代码

文本内容为:

<<<<<<< HEAD
branch dev update
=======
branch dev update
branch test update 1
branch test update 2
>>>>>>> test commit
复制代码

修改为:

branch dev update
branch test update 1
branch test update 2
复制代码
$ git add test.txt
$ git rebase --continue
Applying: test commit
$ git branch
  dev
  feature
  master
* test
复制代码

可见当前分支也自动切换到了 test,说明这条指令由于已经指明了谁要 rebase 到谁,和当前在什么分支完全无关。

git rebase --onto [目标分支] [排除分支] [被 rebase 分支]

切换到 dev 分支,此时 B 的内容为:

branch dev update
复制代码

修改内容并提交:

branch dev update second
hahahaha
复制代码
$ git commit -a -m 'dev commit 2'
复制代码

现在想将 dev 分支内容 rebase 进 feature,但又想排除也在 test 分支的内容,即希望将在 dev 而不在 test 的提交,即 H 的内容在 feature 重做,希望 feature 分支文本内容变成:

branch feature update
branch dev update second
hahahaha
复制代码

而不要有 B 的内容 branch dev update,这应该也和当前所处分支没关系,直接在 dev 上执行:

$ git rebase --onto feature test dev
First, rewinding head to replay your work on top of it...
Applying: dev commit 2
error: Failed to merge in the changes.
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
Patch failed at 0001 dev commit 2
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
复制代码

解决冲突:

<<<<<<< HEAD
branch feature update
=======
branch dev update second
hahahaha
>>>>>>> dev commit 2
复制代码

变成:

branch feature update
branch dev update second
hahahaha
复制代码

现在的状态是:

一个应用

公司的电脑编译 Android 项目,七八分钟甚而要十分钟才能成功,而且一编译内存就占满,电脑卡的什么都做不了,许多时间都耗在编译上了,所以接入了阿里的 Freeline 框架,但是并不想把这种引入放到协作的分支上,别人用 Mac 没那么慢,而且不允许随便引入框架,所以自己本地单独建立分支引入 Freeline,比如 feature-freeline 分支。

这样在协作分支 develop 有更新修改,我每次都 merge 到 feature-freeline 分支,这样将来比较两个分支,feature-freeline 有大量的合并指向两个父提交的提交

而假设使用 rebase,就可以保证每次 feature-freeline 指向的提交紧跟在 develop 提交的后面,rebase 后 develop 分支不要 merge,就在原来的地方开发,这样分叉开来,需要编译时再次 rebase。

PS:这只能做一个例子,实际上后来发现也不能每次要编译了,都得先提交再 rebase 啊

转载于:https://juejin.im/post/5ac5aefff265da239c7b967f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值