1. Git的分支
Git的分支,其实本质上仅仅是指向提交对象的可变指针。它会在每次的提交操作中自动向前移动。
由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。创建一个新分支就像是往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单能不快吗?
2. HEAD指针
Git 是怎么知道当前在哪一个分支上呢?很简单,它有一个名为 HEAD
的特殊指针。请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的 HEAD
概念完全不同。在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD
想象为当前分支的别名)。
查看各个分支当前所指的对象
$ git log --oneline --decorate
如果需要查看每一个分支的最后一次提交,可以运行 git branch -v
命令。
如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged
查看所有包含未合并工作的分支,可以运行 git branch --no-merged
3. 远程分支
3.1 远程引用(Remote references)
远程引用(Remote references)指的是在远程仓库中的引用(指针),包括分支、标签等。可以通过git ls-remote [remote]
或git remote show [remote]
命令查看远程引用。
然而,更常用的方式是利用远程跟踪分支。
3.2 远程跟踪分支(Remote-tracking branches)
远程跟踪分支(Remote-tracking branches)是对远程分支状态的引用。
它们存储在本地,不能移动。只有在本地仓库与远程仓库进行网络通信时,由Git自动移动它们。远程跟踪分支像是你上次连接到远程仓库时,那些分支所处状态的书签。
它们以(remote)/(branch)形式命名。
3.3 跟踪分支(Tracking Branches)
从一个远程跟踪分支检出一个本地分支会自动创建一个叫做 “跟踪分支”(有时候也叫做“上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入 git pull,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支。
3.3.1 检出跟踪分支
$ git checkout --track origin/branchname
# 等价于
$ git checkout -b branchname origin/branchname
3.3.2 显式设置跟踪远程分支
$ git branch --set-upstream-to origin/branchname
# 等价于
$ git branch -u origin/branchname
3.3.3 查看设置的所有跟踪分支
$ git branch -vv
需要重点注意的一点是这些数字的值来自于你从每个服务器上最后一次抓取的数据。这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。可以像这样做:
$ git fetch --all
$ git branch -vv
4 同步变化
4.1 git push
与 git pull
语法:
$ git push <远程主机名> <本地分支名>:<远程分支名>
分支推送顺序的写法是<来源地>:<目的地>, 所以,
git pull
是<远程分支>:<本地分支>,git push
是<本地分支>:<远程分支>。
当本地分支是跟踪分支时,命令即可省略为 git push
和 git pull
。
4.2 新建分支
- 新建远程分支
$ git push -u origin newBranch:newBranch // 新建远程分支
如果省略远程分支名,则表示将本地分支推送与之存在“追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
如git push origin master
省略了远程分支,其中 origin
是远程主机名,master
是本地分支,所以,命令表示,将本地的master
分支推送到远程主机的master
分支。意思也为:“取出我在本地的master
分支,推送到远程仓库的master
分支中去”。因此它与运行git push origin master:master
是一样的效果。
- 新建本地分支
$ git branch newBranch //新建本地分支newBranch
$ git checkout -b newBranch //新建并切换到本地分支newBranch
4.3 删除分支
- 删除远程分支
$ git push origin :remoteb1
git push
命令如果省略本地分支名,将删除远程分支。如git push origin :remoteb1
将删除远程remoteb1
这个分支。
- 删除本地分支
$ git branch -d 本地分支名
5. 分支的合并(get merge
)
语法:git merge [要合并进来的分支名]
如当前所在分支是release
,则执行以下
$ git merge b1
表示,将分支b1
合并到当前分支release
。
5.1 快进(fast-forward)
当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
5.2 “三方合并”生成一个合并提交
在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。
和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
需要指出的是,Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础。
项目分叉历史
$ git log --oneline --decorate --graph --all
5.3 冲突
修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突。此时 Git 做了合并,但是没有自动地创建一个新的合并提交。Git 会暂停下来,等待你去解决合并产生的冲突。你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件。
使用图形化工具来解决冲突,你可以运行 git mergetool
,该命令会为你启动一个合适的可视化合并工具,并带领你一步一步解决这些冲突:
$ git mergetool