Learn Git Branching 学习笔记

Learn Git Branching

Learn Git Branching

基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
level intro1: git commit

level intro2: git branch; git checkout -b

level intro3: git merge <Commit>; git merge <Branch>;

level intro4: git rebase <Commit>; git rebase <Branch>

level rampup1: HEAD; git checkout <Branch>; git checkout <Commit>; commit in detached head

level rampup2: Relative Refs; git checkout <Commit>; git checkout HEAD^;

level rampup3: Relative Refs; git checkout HEAD~4; git branch -f <Branch> HEAD~3; git branch -f <Branch> <Commit>; git branch -f <Branch> <Branch>;

level rampup4: git reset; git revert <Commit>;

level move1: git cherry-pick <Commit1> <Commit2> <...>

level move2: git rebase -i <Commit>; git rebase -i <Branch>

level mixed1: git cherry-pick; git rebase -i;
用于:忽略debug/print的提交,只move最终的结果到主分支上

level mixed2: git rebase -i;(调整需要修改的commit到最前) git commit --amend;(修补提交,抛弃原有的提交结果,替换为新的提交) git rebase -i;(调整回原来的commit顺序)
用于:在更早之前的提交结果中做修改,并提交

level mixed3: git checkout;(跳到需要修改的提交结果C2的父节点C1) git cherry-pick;(move需要修改的提交结果C2) git commit --amend;(修补提交,抛弃原有的提交结果C2,替换为新的提交C2') git cherry-pick;(将原来C2之后的提交结果move到C2'之后)
用于:在更早之前的提交结果中做修改,并提交

level mixed4: git tag <Tag> <Commit>; 直接在tag上commit或checkout,实际上不是针对tag,而是针对对应的commit

level mixed5: git describe <ref>; 输出: <tag>_<numCommits>_g<hash>
用于:查看目标分支/commit在历史上最近的祖先tag

level advanced1: 将多个分支调整为顺序排列

level advanced2: ~和^都可以填写数字,merge的结果C1'是同时指向两个父节点C0和C1的,遇到C1'的时候向上checkout默认是按着第一个父节点C1向上移动HEAD,git checkout <branch>^2 可以使其移动到第二个父节点C0。以下命令:git checkout HEAD~;git checkout HEAD^2;git checkout HEAD~2;可以合并为git checkout HEAD~^2~2

level advanced3: 调整分支,使不同分支分别指向不同版本

1、git merge <Commit>; git merge <Branch>:指向分支1,将分支1和分支2的提交结果C1和C2合并为新的提交C3,该提交同时指向C1和C2,分支1指向C3,分支2仍指向C2。如果C1是C2的祖先,则结果为分支1指向C2(fast forwarding)。

2、git branch -f <Branch> <Commit>:可以直接将某个分支(不能是当前分支)指向任意一个commit点,而不需要先跳到该分支。

3、git revert <Commit>:生成一个新的提交来撤销某次提交,此次提交之前的commit都会被保留。

假设C1添加了文件f1,C2在C1的基础上添加了文件f2,C3是在C2的基础上git revert生成的新提交:如果git revert C2,那么在C3可以看到f1;如果git revert C1,那么在C3可以看到f1和f2,这是因为C2是基于C1的,虽然在C3看来是撤销了C1的提交,但是C2中仍然有f1文件。

假设C1添加了文件f1,C2添加了文件f2,C1’是在C1上执行git rebase master合并结果,C3是在C1’的基础上git revert生成的新提交:如果git revert C1',那么在C3可以看到f2;如果git revert C2,那么在C3可以看到f1。

4、git cherry-pick <Commit1> <Commit2> <...>:指向某分支1,将其他选中的提交结果move到分支1下。如果选中目标有分支2,move的是分支2对应的提交结果,分支2的指向不会被改变。如果选中的提交结果中是分支1(HEAD)的祖先节点,则abort,所有结果都不会被move。

5、git rebase <Commit>; git rebase <Branch>:指向某分支1,将分支1所有新工作move到分支2(或某个commit)下,如果目标是祖先节点,则不会move。

6、git rebase -i <Commit>; git rebase -i <Branch>:指向某分支1,将【分支1】到【分支1和分支2(或某个commit)的共同祖先(分支2/commit的祖先可以是自己)】之间分支1上的所有工作(不包括共同祖先本身)move到分支2(或某个commit)下,如果目标是祖先节点,也会move,可以调整和选择分支1上要move的工作。

如果涉及时间线合并,分支2(或某个commit)不是合并的时间线的一员,则将【分支1】到【分支1和分支2(或某个commit)的共同祖先(分支2/commit的祖先可以是自己)】之间【所有合并时间线的所有工作】(不包括共同祖先本身、合并节点)move到分支2(或某个commit)下,如果目标是祖先节点,也会move,可以调整和选择要move的工作。

执行git rebase -i b2之后可move的节点如下:

如果涉及时间线合并,分支2(或某个commit)是合并的时间线的一员,则将【分支1】到【分支1和分支2(或某个commit)的共同祖先(分支2/commit的祖先可以是自己)】之间【分支2(或某个commit)时间线之后的工作和其他合并时间线的所有工作】(不包括共同祖先本身、合并节点)move到分支2(或某个commit)下,如果目标是祖先节点,也会move,可以调整和选择要move的工作。

执行git rebase -i C3之后可move的节点如下:

7、level mixed2示意图:

8、level mixed3示意图:

进阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
level remote1: git clone; 

level remote2: git clone; git checkout origin/master

level remote3: git fetch;

level remote4: git pull;

level remote5: git fakeTeamwork; git fakeTeamwork master 3; 伪装远程更新

level remote6: git push;

level remote7:
1. git fetch; git rebase origin/master; git push;
2. git fetch; git merge origin/master; git push;
3. git pull --rebase; git push;
4. git pull; git push;
方法1等同于方法3;方法2等同于方法4,多了一个合并

level remoteAdvanced1: git rebase 处理多分支合并

level remoteAdvanced2: git merge 处理多分支合并
more rebase, less merge;
rebase: clean commit, but modify commit history;

level remoteAdvanced3:
1. git checkout -b newB origin/master;
2. git branch -u origin/master newB;
创新并新建分支newB,并设置其跟踪远端分支master

level remoteAdvanced4: git push <remote> <place>

level remoteAdvanced5: git push origin <source>:<destination>

level remoteAdvanced6: git fetch <remote> <place>; git fetch origin <source>:<destination>

level remoteAdvanced7: git push origin :<destination>; git fetch origin :<destination>

level remoteAdvanced8: git pull <remote> <place>; git pull origin <source>:<destination>

1、git clone:本地仓库会出现两种分支,一种是本地分支,一种是远端分支,远端分支格式为/。为所有远端的分支创建对应的本地远端分支,为远端当前的活跃分支创建对应的本地分支(一般为master),所以clone之后一般只看到一个分支。

2、git clone; git checkout origin/mastergit clone之后执行git checkout origin/master会导致detached HEAD,因为远端分支只有在remote更新的时候才能更新。

3、git fetch:下载远端所有分支的新提交到本地仓库,更新所有远端分支指向,本地分支指向不变。

4、git pull:git fetch + git merge。指向分支1,下载分支1 track的远端分支(远端分支可同时被多个本地分支track,如果本地分支没有track远端分支,即使远端分支有同名的分支也不会被pull)的新提交到本地仓库,更新当前远端分支指向,merge当前分支和对应的远端分支为新提交结果。如果是detached HEAD,分开执行git fetch + git merge ,最终结果是下载更新并将HEAD fast forward到merge的对象,没有真正merge;如果是直接执行git pull,则失败。

5、git push:将当前分支所有提交结果更新到远端,并且更新本地的远端分支指向到最新提交。如果当前分支已更新到远端,而本地分支的远端分支没有指向本地分支(如本地不存在bar分支,但存在本地的远端分支origin/bar,git fetch origin bar:bar的时候创建并更新了本地分支bar的指向,但没有更新本地分支的远端分支origin/bar指向,则会造成上述现象),执行git push并不会更新本地远端分支指向。

6、git push <remote> <place>
place指的是本地分支,如果该分支没有track远端分支,则会在远端创建对应的分支,并设置本地分支track到该远端分支。如果当前是detached HEAD,执行缺省的git push会失败,但可以执行不缺省的git push <remote> <place>
由于远端分支可同时被本地多个分支track,如果本地分支place所track的远端分支版本更高,则push失败。

7、git push origin <source>:<destination>:将本地某个任意的提交结果source(不一定是最新的)以及之前的工作更新到远端分支;甚至于如果destination是远端不存在的分支,将在远端创建对应分支,并将指定的提交结果source以及之前的工作更新到远端,在本地创建对应的远端分支,并指向source位置。
可用于将本地两个分支的工作交换更新到远端分支,但如果远端分支的版本已经新于本地提交结果source,则push失败。git push origin <source>:<destination>会更新destination对应的本地远端分支指向到source。

上图中,执行git push origin b1:master失败

上图中,执行git push origin HEAD^1:foogit push origin foo:master成功,结果如下:

8、git fetch <remote> <place>; git fetch origin <source>:<destination>git fetch <remote> <place>只下载指定的远端分支工作,并更新本地的远端分支指向;git fetch origin <source>:<destination>不仅下载指定的远端分支工作,还更新对应的本地分支destination指向(指向fetch的最新结果),但不更新destination的远端分支指向,因此git fetch origin <source>:<destination>无法fetch到当前checkout的分支,这是为了不影响当前checkout分支的工作。git fetch origin <source>:<destination>中source是远端仓库某个提交结果,destination是本地分支,如果本地不存在destination分支,将创建该分支,但不会创建对应的远端分支,假如本地原来存在远端分支,该新建本地分支也没有track到远端分支。

9、git push origin :<destination>; git fetch origin :<destination>git push origin :<destination>表示将本地“nothing”更新到远端分支,其结果是删除了远端分支和本地的远端分支(不删除本地分支)。git fetch origin :<destination>表示下载“nothing”到本地,更新对应的本地分支destination指向,但不更新本地的远端分支指向,其结果是在本地创建本地分支destination(指向当前HEAD指向的位置),但不创建destination的远端分支,如果本地已存在本地分支destination,则执行失败。

10、git pull <remote> <place>; git pull origin <source>:<destination>git pull origin foo等同于git fetch origin foo+git merge o/foogit pull origin bar~1:bugFix等同于git fetch origin bar~1:bugFix+git merge bugFix,注意前者将调整本地的远端分支到下载的最新提交结果,后者将调整本地分支到下载的最新提交结果,而merge的对象是最新的提交结果和当前checkout分支。
可用于解决以下情况:本地主分支和远端分支的工作完全不同,且远端有其他分支,该情况下在本地合并远端工作和本地工作。思路是:由于本地分支和远端分支的工作完全不同,将远端分支fetch到本地时使用新的分支指向它,就像是在本地把该分支当作完全崭新的分支来对待。如下:

执行git pull origin bar:foo+git pull origin master:side之后:

显示 Gitment 评论