git简明教程 - 技巧篇

这一篇会介绍git的一些常用技巧,开发中经常会遇到的问题,让我们感受git的强大之处。

cherry-pick

我直接把它翻译成’摘樱桃’可以不?

git cherry-pick可以选择某一个分支中的一个或几个commit(s)来进行操作。假设我们有个稳定版本的分支master, 另外还有个开发版本的分支dev,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个dev中的功能到master中, 这里就可以使用cherry-pick了,其实也就是对已经存在的commit 进行再次提交。

简单用法:

1
git cherry-pick <commit id>

注意:当执行完 cherry-pick 以后,将会生成一个新的提交;这个新的提交的哈希值和原来的不同,但描述一样;

做一个简单演示,一个readme.txt文件、两个分支(master/dev)

master的提交历史如下:

1
2
3
$ git log --pretty=oneline
4bf41302efc10dd4a6e85c35ec8ef02b1b32e633 (HEAD -> master) second
1e2196ba347c4338bfa7589aabe2513ecabeeedd first

readme内容:

1
2
11111111111
22222222222222

dev的提交历史如下:

1
2
3
4
5
6
$ git log --pretty=oneline
16ae2df968afd92b89f97171480d764ec607deaf (HEAD -> dev) dev 5566
a25a08a678d55f7fa60c4185c2db38258aa9a700 dev 44444
9dc6d54e0357bfde4ccdc0b81888079d4fd4f585 dev 333
4bf41302efc10dd4a6e85c35ec8ef02b1b32e633 (master) second
1e2196ba347c4338bfa7589aabe2513ecabeeedd first

readme内容:

1
2
3
4
5
6
11111111111
22222222222222
dev 3333333333333
dev 44444444444444
dev 55555555555555555
dev 6666666666666666666

下面,我要把dev分支的16ae2df9合并到master中。

1
2
git checkout master
git cherry-pick 16ae2df9

如果出现冲突:

1
2
3
4
$ git cherry-pick a25a08...16ae2df9
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit | --abort)"
fatal: cherry-pick failed

先使用git cherry-pick --abort停掉之前的,然后再执行cherry-pick命令。

如果出现冲突:

1
2
3
4
5
$ git cherry-pick 16ae2df9
error: could not apply 16ae2df... dev 5566
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

解决办法是先使用status命令看看什么文件冲突了:

1
2
3
4
5
6
7
8
9
10
11
12
$ git status
On branch master
You are currently cherry-picking commit a25a08a.
(fix conflicts and run "git cherry-pick --continue")
(use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

打开readme文件看看:

1
2
3
4
5
6
7
8
9
11111111111
22222222222222
<<<<<<< HEAD
=======
dev 3333333333333
dev 44444444444444
dev 55555555555555555
dev 6666666666666666666
>>>>>>> 16ae2df... dev 5566

这里一开始我还有点疑惑,为什么会产生冲突呢,后来在知乎上找到了答案。

https://www.zhihu.com/question/27804571

cherry-pick只会将一个commit引入的更改应用到当前的commit上。而命令只会将从d1->d2的修改引入到m1中,这样当然会造成冲突。

如果只是想让master恢复到某个commit上,又不想有冲突,可使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git merge 16ae2df9 --squash
Updating 4bf4130..16ae2df
Fast-forward
Squash commit -- not updating HEAD
readme.txt | 4 ++++
1 file changed, 4 insertions(+)

$ git commit -m "merge"
[master a3a8255] merge
1 file changed, 4 insertions(+)

$ cat readme.txt
11111111111
22222222222222
dev 3333333333333
dev 44444444444444
dev 55555555555555555
dev 6666666666666666666

接下来我再试试区间

1
2
3
4
# 下面是左开右闭区间
git cherry_pick <start-commit-id>...<end-commit-id>
# 下面是左右闭区间
git cherry-pick <start-commit-id>^...<end-commit-id>

基本一样的逻辑。