Git —— merge, cherry-pick, rebase

1. merge explore

  You may read my article Git inside before. I hope it’ll help you to know Git in essence.

  (1) test case 1: merge without conflict

~ $ mkdir beta
~ $ cd beta
~ $ git init
~ $ printf '1' > number.txt
~ $ printf 'a' > letter.txt
~ $ git add number.txt letter.txt
~ $ git commit -m 'a1'
~ $ git checkout -b dev
...... 
make some commit in dev branch
......
~ $ printf 'c' > letter.txt
~ $ git add letter.txt
~ $ git commit -m 'c1'
// switch back to master branch
~ $ git checkout master
......
make some commit in master commit
......
~ $ printf '4' > number.txt
~ $ git add number.txt
~ $ git commit -m 'a4'
// switch to dev branch
~ $ git merge master

  In branch master, we changed a file content (number.txt) .
  In branch dev , we changed another different file (letter.txt).
  Then after some commits, we do git merge master. dev as receiver, master as giver. The GIt get a blob object (bf) from master HEAD commit, and get another blob object (34) from dev HEAD commit, and merge a new commit without conflict.
  Of course, you can add more commit in each branch as you want. And you also can change both file in any commit. But at this time, you should make sure the last commit version will not change both file. Because Git just compare three version commit : the common parent (base), master HEAD commit, dev HEAD commit. And take the change blob objects. So if you change a file in both branch, then Git confuse what to pick, thus conflict, ask you for help. As show in Fig 1.


这里写图片描述
Fig 1. Merge without conflict

  (2) test case 2 : merge conflict

~ $ git checkout dev
~ $ printf 'd' > letter.txt
~ $ git add letter.txt
~ $ git commmit -m 'd4'
// checkout master branch, modify letter.txt also
~ $ git checkout master
~ $ printf 'e' > letter.txt
~ $ git add letter.txt
~ $ git commit -m 'e4'
~ $ git checkout dev
~ $ git merge master // merge conflict in letter.txt
// resolve conflict
~ $ printf 'de' > letter.txt
~ $ git add letter.txt
~ $ git commit -m 'de4'

  when merge conflict, the index looks like this.

100644 2e65efe2a145dda7ee51d1741299f848e5bf752e 1   letter.txt
100644 c59d9b6344f1af00e504ba698129f07a34bbed8d 2   letter.txt
100644 9cbe6ea56f225388ae614c419249bfc6d734cc30 3   letter.txt
100644 bf0d87ab1b2b0ec1a11a3973d2845b42413d9767 0   number.txt
  • The entry for number.txt at stage 0 is the same as it was before the merge. (no conflict)
  • The entry for stage 1 has the hash of the base letter.txt content.
  • The entry for stage 2 has the hash of the receiver (dev) letter.txt content.
  • The entry for stage 3 has the hash of the giver (master) letter.txt content.
  • The presence of these three entries tells Git that letter.txt is in conflict.

  we modify file letter.txt content in both branch. so it make conflict. we resolve conflict by print ‘de’ content to letter.txt. of course, you can resolve conflict as you will. show in Fig 2.


这里写图片描述
Fig 2. Merge conflict

2. cherry-pick explore

  I’ve already mentioned (back on the page about Garbage Collection) that a Git commit’s ID is a hash of both its contents and its history. So, even if you have two commits that introduce the exact same change, if they point to different parent commits, they’ll have different IDs.
  What git cherry-pick does, basically, is take a commit from somewhere else, and “play it back” wherever you are right now. Because this introduces the same change with a different parent, Git builds a new commit with a different ID. [3] [ 3 ]


  As noted above, both for a cherry-pick and for a rebase, BASE is the parent (C^) of the the commit C being pulled in. In the general case C^ isn’t a common ancestor, so why call it BASE? (In a normal merge BASE is a common ancestor. And part of git’s successes in merging are due to its ability to find a good common ancestor.)
  Essentially, one does this as a way to implement “patch” functionality via the normal three-way merge algorithm. In particular you get these “patchy” properties: [4] [ 4 ]

(1) test case 1 : cherry-pick without conflict

~ $ mkdir testcherry
~ $ git init
~ $ printf '0' > number.txt
~ $ git add number.txt
~ $ git commit -m '0'
~ $ printf '1' > number.txt
..... do some commit 
~ $ git checkout 487a6b3 -b dev //from commit 0 checkout dev
~ $ printf '2' > number.txt
~ $ git add number.txt
~ $ git commit -m 'dev-2'
~ $ printf '3' > number.txt
~ $ git add number.txt
~ $ git commit -m 'dev-3'

  if we do git merge master, obviously, it’ll cause merge conflict. the index looks like this.

100644 c227083464fb9af8955c90d2924774ee50abb547 1   number.txt
100644 e440e5c842586965a7fb77deda2eca68612b1f53 2   number.txt
100644 d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 3   number.txt

   we do git cherry hash

~ $ git checkout master 
~ $ git cherry-pick af052b8

  As it show in Fig 3. after cherry-pick, get a new commit (5f). unlike merge, the BASE is different, and new commit has only one parent (83).


这里写图片描述
Fig 3. Git cherry without conflict

(2) test case 2 : cherry-pick conflict

~ $ checkout dev
~ $ printf '4' > number.txt
~ $ git add number.txt
~ $ git commit -m 'dev-4'
~ $ git checkout master
~ $ git cherry-pick a09c286  // dev-4

  because conflict, so cherry-pick stopped with error: could not apply a09c286… dev-4. the index looks like this.

100644 e440e5c842586965a7fb77deda2eca68612b1f53 1   number.txt
100644 d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 2   number.txt
100644 bf0d87ab1b2b0ec1a11a3973d2845b42413d9767 3   number.txt

  we simply do printf '24' > number.txt; git add number.txt; git commit -m '24' to resolve conflict. As show in Fig 4.


这里写图片描述
FIg 4. cherry-pick conflict

3. rebase explore

  With the rebase command, you can take all the changes that were committed on one branch and replay them on another one.
  It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. [5] [ 5 ]

  in above Fig 4, if we do git rebase master instead cherry-pick, then we got the commit graph similar to Fig 5.


这里写图片描述
Fig 5. git rebase

  we can use git cherry-pick to simulate git rebase. first, cherry-pick c0, BASE is 48, no change. second, cherry-pick af, BASE is c0, apply change, last, cherry-pick a0, BASE is af, apply change.

4. summary

  • (a) Perhaps merge is better than cherry-pick or rebase. (refs:no-cherry-picking  argue-for-merge)
  • (b) cherry-pick BASE is the parent of cherrying id, not common ancestor.
  • (c) git compare the receiver to BASE, compare the giver to BASE, and then decide what to be changed.

Reference

[1] merge_vs_rebase_why_not_cherrypick
[2] git-cherry-pick-lvs-rebase
[3] think-like-a-git
[4] what-is-cherry-pick-ancestor
[5] Git-branching-rebasing

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值