git之merge和rebase

写在前面

不管是merge,还是rebase都是将一个分支的修改合并到另一个分支,但是方式方法有所不同,下面我们具体来看下每种情况。

1:merge

merge就是合并代码,这种方式合并代码后,合并的结果会生成一个新的commit(效果同手动修改后的提交),而merge又有两种方式分别是fast-forward和,非fast-forward,分别来看下这两种方式。

1.1:fast-forward

当合并代码的时候,如果是没有冲突(更准确的表述应该是文件不需要合并时),git就会默认采用这种方式,接下来看下具体操作。

  • 基于master创建分支dev100
$ git checkout -b dev100
Switched to a new branch 'dev100'

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git push origin dev100:dev100
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
remote: Create a pull request for 'dev100' on Gitee by visiting:
remote:     https://gitee.com/dongsir2020/test-cherry-pick/pull/new/dongsir2020:dev100...dongsir2020:master
To https://gitee.com/dongsir2020/test-cherry-pick.git
 * [new branch]      dev100 -> dev100

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git branch --set-upstream-to=origin/dev100
Branch 'dev100' set up to track remote branch 'dev100' from 'origin'.

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git branch -vv
  dev    1d2f221 dev first commit
  dev10  09ea18b [origin/dev10] dev second add content
* dev100 09ea18b [origin/dev100] dev second add content
  master 09ea18b [origin/master] dev second add content

命令git branch --set-upstream-to=origin/dev100是用来关联本地的dev100到远程的dev100分支。

  • 在dev100分支执行若干次提交
JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ touch dev100.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git status
On branch dev100
Your branch is up to date with 'origin/dev100'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        dev100.txt

nothing added to commit but untracked files present (use "git add" to track)

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git add dev100.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git commit -am'add dev100.txt'
[dev100 cebe7f0] add dev100.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 dev100.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ vim dev100.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git commit -am'add some content in dev100.txt'
warning: LF will be replaced by CRLF in dev100.txt.
The file will have its original line endings in your working directory
[dev100 8adeee0] add some content in dev100.txt
 1 file changed, 1 insertion(+)

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git status
On branch dev100
Your branch is ahead of 'origin/dev100' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev100)
$ git push origin dev100:dev100
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 459 bytes | 459.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To https://gitee.com/dongsir2020/test-cherry-pick.git
   09ea18b..8adeee0  dev100 -> dev100

此时相比master新增的提交如下:

$ git log -n2 --pretty=oneline
8adeee00933d01888f128ced8193e728f99faf1f (HEAD -> dev100, origin/dev100) add some content in dev100.txt
cebe7f0df20707a6a55419a310671c5fb3fc1b95 add dev100.txt
  • 切换到master,并执行merge
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (master)
$ git branch
  dev
  dev10
  dev100
* master

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (master)
$ git merge dev100
Updating 09ea18b..8adeee0
Fast-forward
 dev100.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 dev100.txt 

通过输出Fast-forward就可以知道使用了fast forward方式,此时查看master的历史记录:

$ git log --pretty=oneline -n4 --graph
* 8adeee00933d01888f128ced8193e728f99faf1f (HEAD -> master, origin/dev100, dev100) add some content in dev100.txt
* cebe7f0df20707a6a55419a310671c5fb3fc1b95 add dev100.txt
* 09ea18b4302ede38ddc42e7051e14593c1862e9a (origin/master, origin/dev10, origin/HEAD, dev10) dev second add content
* 8497aa644b7c63d4c1d59b5cd9a1bf9dc19eb136 dev first commit

提交记录是有的,但是并不能体现出到底是从哪个分支合并过来的代码其实仅仅是改变了指针的指向,这不利于我们维护代码,因为有时这些信息对我们还是很有用的,此时就可以考虑使用非fast forward的方式,我们继续往下看。

1.2:none fast-forward

想要使用none fast-forward方式也比较简单,只需要在merge时增加参数–no-ff即可,如下实验过程。

  • 基于master创建分支dev101
$ git branch
  dev
  dev10
  dev100
* master

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (master)
$ git checkout -b dev101
Switched to a new branch 'dev101'

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git push origin dev101:dev101
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
remote: Create a pull request for 'dev101' on Gitee by visiting:
remote:     https://gitee.com/dongsir2020/test-cherry-pick/pull/new/dongsir2020:dev101...dongsir2020:master
To https://gitee.com/dongsir2020/test-cherry-pick.git
 * [new branch]      dev101 -> dev101

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git branch --set-upstream-to=origin/dev101
Branch 'dev101' set up to track remote branch 'dev101' from 'origin'.

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git branch -vv
  dev    1d2f221 dev first commit
  dev10  09ea18b [origin/dev10] dev second add content
  dev100 8adeee0 [origin/dev100] add some content in dev100.txt
* dev101 8adeee0 [origin/dev101] add some content in dev100.txt
  master 8adeee0 [origin/master] add some content in dev100.txt
  • 在dev101分支执行若干次提交
$ touch 101.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git add *

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git commit -am'add 101.txt'
[dev101 065f581] add 101.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 101.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ vim 101.txt

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git commit -am'add some content in 101.txt'
warning: LF will be replaced by CRLF in 101.txt.
The file will have its original line endings in your working directory
[dev101 0e2c8a0] add some content in 101.txt
 1 file changed, 1 insertion(+)

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev101)
$ git push origin dev101:dev101
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 488 bytes | 488.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To https://gitee.com/dongsir2020/test-cherry-pick.git
   8adeee0..0e2c8a0  dev101 -> dev101
  • 切换到master,使用none fast-forward方式合并
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (master)
$ git merge dev101 --no-ff
Merge made by the 'recursive' strategy.
 101.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 101.txt

注意git merge dev101 --no-ff时会弹出一个窗口让我们录入生成的新提交的注释信息,我这里录入的是这是--no-ff方式合并产生的新提交的comment,此时log如下:

$ git log -n4 --pretty=oneline --graph
*   291d49a04f24dd2f266d74d7f1f63406c2632c1e (HEAD -> master) 这是--no-ff方式合并产生的新提交的comment
|\
| * 0e2c8a01e24715b45386b10f0a75b6b4d6215c07 (origin/dev101, dev101) add some content in 101.txt
| * 065f5815cf2505c1ff0feb4f0931056211f42ba2 add 101.txt
|/
* 8adeee00933d01888f128ced8193e728f99faf1f (origin/master, origin/dev100, origin/HEAD, dev100) add some content in dev100.txt

可以看到保留了合并分支的信息,并生成了一个提交的日志291d49a04f24dd2f266d74d7f1f63406c2632c1e (HEAD -> master) 这是--no-ff方式合并产生的新提交的comment。个人觉得这种方式比较好,很清晰。

git rebase有时怎样呢?我们继续来一起看下。

2:rebase

rebase翻译过来就是变基,那么这个基是什么呢,比如当前master分支的提交状态如下:

在这里插入图片描述

然后我们使用命令git checkout -b feature,创建了feature分支,然后有执行了提交B1,B2(产生提交并不必须,只是为了好描述),此时如下图:

在这里插入图片描述

那么此时feature分支的基就是A3,知道了什么是基,变基就清晰了,就是改变这个基,而改变这个基的命令就是git rebase。这种操作个人认为最大的坏处就是破坏了实际的提交记录,改变了代码提交实际的时间线,我们通过一个实际的例子来看下。

首先从master创建test,dev两个分支,然后分别在两个分支上执行2次提交(注意提交内容在合并时不要产生冲突,这里不考虑rebase有冲突的情况),变为下图:

在这里插入图片描述

准备完毕后,查询test,dev两个分支的提交记录如下:

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev)
$ git log --pretty=oneline -n2
90f470ed681d6ecdc43cbddbea6b846714cc7a7f (HEAD -> dev) dev2
4a386b2c4d4974d0b9aaa650540a4deb589a25f1 dev1
----------------华丽的分割线--------------
JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (test)
$ git log -n2 --pretty=oneline
620f696b01e25185185eddc8661c533f59aabcac (HEAD -> test) test2
16c55db40582ac77d8f76e18a44583bcf3369499 test1

接下来我们合并test分支到dev分支,只需要让dev变基到test即可,执行如下操作:

$ git checkout dev
Switched to branch 'dev'

JHP+Administrator@jhp MINGW64 /d/test/test-cherry-pick (dev)
$ git rebase test
Successfully rebased and updated refs/heads/dev.

此时查看dev的提交日志:

$ git log -n4 --pretty=oneline
b7b9b230af21686b822ebe80edb6ed71d97c488e (HEAD -> dev) dev2
cfdc1108b0d42a1b516a1fed8310cc438e1d866e dev1
620f696b01e25185185eddc8661c533f59aabcac (test) test2
16c55db40582ac77d8f76e18a44583bcf3369499 test1

可以看到test的提交日志插入到了dev自身提交日志之前,如果魔法一般,这就是变基的结果,此时结构变为下图:

在这里插入图片描述

2:总结

merge fast-forward方式是在合并代码时,不需要真正的合并文件就能完成合并时采用的方式,通过简单的移动指针达到效果,合并效率高,但是因为没有真正的合并,所以不会产生合并的记录,即不会有分叉产生,一旦需要合并文件(自然包括有冲突的情况)就会使用none fast forward方式,也可以通过参数–no-ff禁用fast forward,这样保证不管什么情况都是明显的合并记录生成。
rebase因为变基,所以会改变代码提交的时间线,修改程序的实际的提交记录,让代码管理陷入混乱,虽然解决了合并产生的复杂分叉的问题,但是引入的问题也更多,出现问题时,无法正常的通过代码提交查找问题,无法正常的回滚代码等,所以建议非必要就不要使用rebase方式合并代码,而是使用merge配合–no-ff参数的方式,这样能够在保证保留了足够多代码合并信息的同时,也不会改变已有的代码提交历史,唯一的不足的可能就是多了一次因合并而产生的commit而已,不过这又有什么问题呢!

写在后面

参考网址:

8.Git merge之 Fast Forward和 No Fast Forward(–no-ff方式)解析

【学了就忘】Git操作 — 65.rebase实战

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值