写在前面
不管是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而已,不过这又有什么问题呢!
。
写在后面
参考网址: