Git Merge
1.merge命令
$ git(test) > git merge feature001 ## 将feature001这个分支合入test分支
2.merge的几种形式
2.1 fast-forward
如下图,当你从master分支拉取一个分支出来进行特性分支的开发,在你开发完成后此时master分支仍是你拉取时的状态
A---B---C feature
/
D---E---F master
这时将feature分支merge到master中,git会默认使用快进的方式,也就是直接将master的HEAD指向C
A---B---C feature
/ master
D---E---F
这种方式当然很简单粗暴,但会带来一个问题,也就是会在master分支中看到feature分支的提交记录(如图中的A/B/C三次提交的提交记录都会展示在master分支中),且回退时也不太好回退。这时就需要使用no-ff模式
实际操作演示:
git checkout -b feature001 ##从test分支中拉取feature分支
## 下面对该分支做两次修改和提交
vi readme.txt
git commit -am"dev feature"
vi readme.txt
git commit -am"dev feature"
git checkout test ##切回test分支
git merge feature001 ##合并feature分支入test,此时git会提示你为fast-forward模式
git log --graph
* commit 02bf77d326664db21533f1406614a293e4ddb483 (HEAD -> test, feature001)
| Author: xxx <xxx@xxx.com>
| Date: Mon Jan 11 00:43:36 2021 +0800
|
| fix feature001
|
* commit 4478047255d76ccf82add89fe7f2a7434caf7699
| Author: xxx <xxx@xxx.com>
| Date: Mon Jan 11 00:42:37 2021 +0800
|
| dev feature001
|
* commit c18240f00cf8eaa28d7cd75ec64e17dfd3858a6b
.....
## 可以看到test和feature指向同一commit且test分支中也包含了feature分支上的commit记录
2.2 no-fast—forward
还是上面那个例子
A---B---C feature
/
D---E---F master
merge时使用git merge --no-ff feature
命令的话,则如下如所示,会产生一个全新的提交G,这样在master上看就看不到ABC三次提交的commit记录了,而且回滚秩序向上回滚一个commit即可
A---B---C feature
/ \
D---E---F-----------G master
因此在你希望集成分支上不包含特性分支上的commit记录时,可以在merge时使用--no-ff参数
实际操作演示:
git checkout -b feature002 ##从test分支中拉取feature分支
## 下面对该分支做两次修改和提交
vi readme.txt
git commit -am"dev feature"
vi readme.txt
git commit -am"dev feature"
git checkout test ##切回test分支
git merge --no-ff feature002 ##合并feature分支入test,此时git会提示你为fast-forward模式
git log --graph
* commit 0baad230d3f5272881a4568b46c66460db771fef (HEAD -> test)
|\ Merge: 02bf77d 5a70aa9
| | Author: xxx <xxx@xxx.com>
| | Date: Mon Jan 11 00:54:42 2021 +0800
| |
| | Merge branch 'feature002' into test
| |
| * commit 5a70aa941faade4571040044bce9234833c3f096 (feature002)
| | Author: xxx <xxx@xxx.com>
| | Date: Mon Jan 11 00:54:28 2021 +0800
| |
| | fix feature002
| |
| * commit 70740239cfcb6bdcda4999f9a00767aed35d0ff1
|/ Author: xxx <xxx@xxx.com>
| Date: Mon Jan 11 00:54:12 2021 +0800
|
| dev feature002
|
* commit 02bf77d326664db21533f1406614a293e4ddb483
...
## 可以很明显的看到,这种模式下test分支不在包含有feature分支的commit记录
Git Rebase
rebase命令
git rebase -i commitID
git rebase branch_name
rebase的使用场景
1.整理commit记录
有时我们可能会有如下诉求,当前分支提交记录如下:
commit 22ad2912ff010751ae35f3963f9b5aa03c8c79c2 (HEAD -> feature002)
Author: xxx <xxx@xxx.com>
Date: Mon Jan 11 01:00:16 2021 +0800
fix feature002
commit 5a70aa941faade4571040044bce9234833c3f096
Author: xxx <xxx@xxx.com>
Date: Mon Jan 11 00:54:28 2021 +0800
fix feature002
commit 70740239cfcb6bdcda4999f9a00767aed35d0ff1
Author: xxx <xxx@xxx.com>
Date: Mon Jan 11 00:54:12 2021 +0800
dev feature002
此时我们想将后面两次bug修复的commit合并为一次,则可以如下操作
git rebase -i 70740239cfcb #以指定的commit为基准,通过不同的指令操作后面的commit
##此时会进入交互模式,在交互模式中将最后一次commit前面的指令从pick修改为s,保存退出即可(此时会进入到下一个交互中,是用来编写合并后的commit信息的)
此时再观察git的提交记录如下
commit 055e66d782c5a7e9fcb2131bc2bd12fd017708dc (HEAD -> feature002)
Author: xxx <xxx@xxx.com>
Date: Mon Jan 11 00:54:28 2021 +0800
fix feature002
fix feature002
commit 70740239cfcb6bdcda4999f9a00767aed35d0ff1
Author: xxx <xxx@xxx.com>
Date: Mon Jan 11 00:54:12 2021 +0800
dev feature002
可以看到此时commit记录已经合并成功!
2.分支合并
1.我们先从 master 分支切出一个 dev 分支,进行开发
D---E---F feature
/
A---B master
2.此时你的同事在你提交之前先提交了另外一个需求
D---E---F feature
/
A---B---C master
3.此时当你想要合并你的分支到master时你就有两个选择:
3.1 直接merge入master
D---E---F feature
/ \
A---B---C-------G master
3.2 先rebase master后再merge入master
D---E---F feature
/ master
A---B---C
可以发现,在rebase之后feature分支的起点变更为了C,也就是这时处于fast-forward可用模式(当然是否使用可以选择),如果追求一个线性的集成分支提交记录,则这种方式可以满足你的需求;
rebase时发生了什么
- 首先,git 会把 feature 分支里面的每个 commit 取消掉;
- 其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
- 然后,把 feature 分支更新到最新的 master 分支;
- 最后,把上面保存的 patch 文件应用到 feature 分支上
冲突解决
在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。注意,你无需执行 git-commit,只要执行 continue
git rebase --continue
这样 git 会继续应用余下的 patch 补丁文件。
ps.在任何时候,我们都可以用 --abort 参数来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态
高危操作
如果你的分支会被他人使用(拉取到本地并基于你的分支开发),那么在你提交到远程仓库之后,不要对已提交内容做rebase操作并强制推送到远程分支;
如果你这样做了,其他人如果使用了你rebase之前的提交作为基点,那么在你rebase并推送之后,对于他的分支而言,相当于丢失了基点(因为rebase会导致commit id变化),示例如下
M1---C1---C2---C3 origin/common
\---C4---C5---C6 common
如果此时common分支rebase了master并强推到远程分支后,
M1---M2---C1'---C2'---C3'origin/common
\---C1---C2---C3
\---C4---C5---C6 common
此时common分支再推送远端时,其最近的公共节点为M1,因为C1-C6会被再次合入 最终在远程分支上可以看到多条重复的commit记录,造成混乱