Learn Git Branching
基础
1 | level intro1: git commit |
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 | level remote1: git clone; |
1、git clone
:本地仓库会出现两种分支,一种是本地分支,一种是远端分支,远端分支格式为
2、git clone; git checkout origin/master
:git 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
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:foo
、git 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/foo
,git 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
之后: