Git merge时使用--no-ff参数

/1、Git:Git-merge的–ff和–no-ff。

前言
Git merge最容易糊涂的地方就是这个--ff参数和--no-ff 参数,通过本文,把这个整理清楚。

其实官网讲的非常清楚,不过可能因为是英文的,所以大家阅读起来会有一些障碍。(PS:其实还是应该逐步逐步提高自己阅读英文文档的能力,想达到一个更高的高度,是需要客服自己本身很多的弱点的)

实例
假设合并前的分支是这样,这个一个非常常见的场景,如果不明白,可以参考另外一篇文章Git Flow工作流:

Git merge时使用--no-ff参数_第1张图片

这是一个很常见的用例,功能开发分支是iss53,在开发新功能,master分支是线上分支,出现了问题,开辟了hotfix分支进行修复,修复完成,进行合并,需要把hotfix合并回master。

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

步骤如下:

切换回master分支。
将hotfix分支合并会master分支。
然后看到了Fast-forward 的字样,这个词组的意思就是快进,播放电影的时候,可以注意一下,快进按钮上面就是这个词组。
那么实际变成了什么样呢?

Git merge时使用--no-ff参数_第2张图片

仅仅是master指针指向了这个提交C4。这样是一种比较快的合并方式,轻量级,简单。
这个时候,我们往往会删掉hotfix分支,因为它的历史作用已经结束,这个时候,我们的iss53这个功能又向前开发,进行了一次提交,到了C5,那么变成了这样:

Git merge时使用--no-ff参数_第3张图片

然后,我们要把iss53 这个分支合并回master,就变成了这样:

这个时候生成了一个新的commit号,这种提交就不是fast-forward(这个时候也无法生成fast-forward提交,因为要将两个版本的内容进行合并,只有在没有需要合并内容的时候,会有这个fast-forward 方式的提交)。
如果我们对第一次合并,使用了--no-ff参数,那么也会产生这样的结果,生成一个新的提交,实际上等于是对C4 进行一次复制,创建一个新的commit,这就是--no-ff的作用。
 

 

2、在很多介绍GItFlow工作流的文章里面,都会推荐在合并分支的时候加上--no-ff参数, 而我们在合并的时候,有时git也会提示 使用了 fast-forward, 这里我将介绍一下merge的三种状态及 git mergegit merge --no-ff 的区别

Git merge的时候,有几种合并方式可以选择

--ff
When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit. This is the default behavior.

--no-ff
Create a merge commit even when the merge resolves as a fast-forward. This is the default behaviour when merging an annotated (and possibly signed) tag.

--squash
--no-squash
Produce the working tree and index state as if a real merge happened (except for the merge information), but do not actually make a commit, move the HEAD, or record $GIT_DIR/MERGE_HEAD (to cause the next git commit command to create a merge commit). This allows you to create a single commit on top of the current branch whose effect is the same as merging another branch (or more in case of an octopus).

With --no-squash perform the merge and commit the result. This option can be used to override --squash.

而我们平常什么都不加的时候,则使用默认的 --ff , 即 fast-forward 方式

看过官方注释后,我们用一张图来简单描画一下相应的行为

 

Git merge时使用--no-ff参数_第4张图片

image.png

 

fast-forward

Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward)不过这种情况如果删除分支,则会丢失merge分支信息。

–squash

把一些不必要commit进行压缩,比如说,你的feature在开发的时候写的commit很乱,那么我们合并的时候不希望把这些历史commit带过来,于是使用–squash进行合并,此时文件已经同合并后一样了,但不移动HEAD,不提交。需要进行一次额外的commit来“总结”一下,然后完成最终的合并。

–no-ff

关闭fast-forward模式,在提交的时候,会创建一个merge的commit信息,然后合并的和master分支
merge的不同行为,向后看,其实最终都会将代码合并到master分支,而区别仅仅只是分支上的简洁清晰的问题,然后,向前看,也就是我们使用reset 的时候,就会发现,不同的行为就带来了不同的影响

 

Git merge时使用--no-ff参数_第5张图片

https://github-1253518569.cos.ap-shanghai.myqcloud.com/2018-09-18_201744.png

 

上图是使用 merge --no-ff的时候的效果,此时git reset HEAD^ --hard 的时候,整个分支会回退到 dev2-commit-2

 

Git merge时使用--no-ff参数_第6张图片

dev3-commit-1

 

上图是使用 fast-forward 模式的时候,即 git merge ,这时候 git reset HEAD^ --hard,整个分支会回退到 dev1-commit-3

通常我们把 master 作为主分支,上面存放的都是比较稳定的代码,提交频率也很低,而 develop 是用来开发特性的,上面会存在许多零碎的提交,快进式合并会把 develop 的提交历史混入到 master 中,搅乱 master 的提交历史。所以如果你根本不在意提交历史,也不爱管 master 干不干净,那么 –no-ff 其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 commint-3,而不是想要的 commit-2,因为 feature 的历史合并进了 master 里。这也就是很多人都会推荐 –no-ff 的原因了吧

3、

通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

下面我们实战一下--no-ff方式的git merge

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git checkout -b dev
Switched to a new branch 'dev'

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (dev)
$ ls
abc.txt  dd  ddd.txt  READMYFILE

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (dev)
$ vi abc.txt

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (dev)
$ git add abc.txt
warning: LF will be replaced by CRLF in abc.txt.
The file will have its original line endings in your working directory.

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (dev)
$ git commit -m 'add merge'
[dev 4fe2eaf] add merge
 1 file changed, 1 insertion(+)

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (dev)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git merge --no-ff -m 'merge with no-ff' dev
Merge made by the 'recursive' strategy.
 abc.txt | 1 +
 1 file changed, 1 insertion(+)

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git log --graph --pretty=oneline --abbrev-commit
*   0b655f8 (HEAD -> master) merge with no-ff
|\
| * 4fe2eaf (dev) add merge
|/
*   d2ac9a9 (origin/master, origin/HEAD) merge
|\
| * 62feb86 feature
* | a1d0ee6 master
|/
*   2360738 conflict fixed
|\
| * 22f91d7 READ
* | 4518e6a by master
|/
*   c01f0f2 Merge branch 'master' of github.com:ZhuBaker/learngit
|\
| * 6af3724 abc
| * ed9c88d dddd
| * 0fefcee dd
| *   1c508f7 Merge branch 'master' of github.com:ZhuBaker/learngit
| |\
| | * 78b85de abc
| * 7e13181 abc
:

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git branch -d dev
Deleted branch dev (was 4fe2eaf).

Administrator@EZ-20170513RQHH MINGW32 /d/workspace/learngit/learn2 (master)
$ git log --graph --pretty=oneline --abbrev-commit
*   0b655f8 (HEAD -> master) merge with no-ff
|\
| * 4fe2eaf add merge
|/
*   d2ac9a9 (origin/master, origin/HEAD) merge
|\
| * 62feb86 feature
* | a1d0ee6 master
|/
*   2360738 conflict fixed
|\
| * 22f91d7 READ
* | 4518e6a by master
|/
*   c01f0f2 Merge branch 'master' of github.com:ZhuBaker/learngit
|\
| * 6af3724 abc
| * ed9c88d dddd
| * 0fefcee dd
| *   1c508f7 Merge branch 'master' of github.com:ZhuBaker/learngit
| |\
| | * 78b85de abc
| * 7e13181 abc
:

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

Git merge时使用--no-ff参数_第7张图片

小结

Git分支十分强大,在团队开发中应该充分应用。

合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。



 

你可能感兴趣的:(Git)