之前已经学会了分支的创建,删除,转换和合并,那么如何对分支进行管理呢
分支的管理
查看分支:git branch
之前用到过 git branch 创建分支,删除分支,直接使用 git branch 就是查看有哪些分支。
其中,有 * 代表是当前工作分支。
查看各个分支最后一次提交信息:git branch -v
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
从清单中筛选出你已经(或尚未)与当前分支合并的分支,加上 --merge
和 --no-merged
选项
$ git branch --merged
iss53
* master
之前我们已经合并了 iss53
,所以在这里会看到它。一般来说,列表中没有 *
的分支通常都可以用 git branch -d
来删掉。因为既然已经把它们所包含的工作整合到了其他分支,删掉也不会损失什么。
而对于 --no-merged 的分支则不能 -d 直接删除,会报错(提示会丢失数据)。如果想要强制删除,使用 -D 选项。
远程分支
牢记这些分支全部都是本地分支,这一点很重要。当你在使用分支及合并的时候,一切都是在你自己的 Git 仓库中进行的 — 完全不涉及与服务器的交互。
远程分支(remote branch)是对远程仓库中的分支的索引。使用 (远程仓库名)/(分支名)
这样的形式表示远程分支,远程分支不能直接修改,只有与网络交互才能对他更新。
如果从远程仓库中克隆一个项目,Git 会自动将此远程仓库命名为 origin
,并下载其中所有的数据,建立一个指向它的 master
分支的指针,在本地命名为 origin/master
,但你无法在本地更改其数据。接着,Git 建立一个属于你自己的本地 master
分支,始于 origin
上 master
分支相同的位置,然后你可以就此开始工作。
如果团队成员在远程仓库推送了他们的更新, 服务器和本地 master 指针会向前移动,而本地的 origin/master 仍然不变
这时可以使用 git fetch origin 拉取服务器的最新数据并同步到本地仓库,origin/master 会移动到最新位置。
推送本地分支
要想和其他人分享某个本地分支,就需要把它推送到一个你拥有写权限的远程仓库。
将本地分支推送到远端服务器:git push (远程仓库名) (分支名)
$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new branch] serverfix -> serverfix
这会将本地 serverfix 分支推送到远程服务器 serverfix 分支。若想把远程分支叫作 awesomebranch
,可以用 git push origin serverfix:awesomebranch
来推送数据。
接下来,当协作者再次从服务器上获取数据时,他们将得到一个新的远程分支 origin/serverfix
,并指向服务器上 serverfix
所指向的版本:
$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From git@github.com:schacon/simplegit
* [new branch] serverfix -> origin/serverfix
值得注意的是,在 fetch
操作下载好新的远程分支之后仍然无法在本地编辑该远程仓库中的分支。换句话说,在本例中,你不会有一个新的 serverfix
分支,有的只是一个你无法移动的 origin/serverfix
指针。
如果要把该远程分支的内容合并到当前分支,可以运行 git merge origin/serverfix
。如果想要一份自己的 serverfix
来开发,可以在远程分支的基础上分化出一个新的分支来:
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
这会切换到新建的 serverfix
本地分支,其内容同远程分支 origin/serverfix
一致,这样你就可以在里面继续开发了。
跟踪远程分支
从远程分支 checkout
出来的本地分支,称为 跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push
,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull
会获取所有远程索引,并合并到本地分支。
在克隆仓库时,Git 通常会自动创建一个名为 master
的分支来跟踪 origin/master
。这正是 git push
和 git pull
一开始就能正常工作的原因。当然,你可以随心所欲地设定为其它跟踪分支,比如 origin
上除了 master
之外的其它分支。刚才我们已经使用:git checkout -b [分支名] [远程名]/[分支名]
。如果是 1.6.2 以上版本的 Git,还可以用 --track
选项简化:
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
要为本地分支设定不同于远程分支的名字,只需在第一个版本的命令里换个名字:
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "sf"
现在你的本地分支 sf
会自动将推送和抓取数据的位置定位到 origin/serverfix
了。
删除远程分支
如果不再需要某个远程分支了,比如搞定了某个特性并把它合并进了远程的 master
分支(或任何其他存放稳定代码的分支),可以这样来删除它:git push [远程名] :[分支名]
。如果想在服务器上删除 serverfix
分支,运行下面的命令:
$ git push origin :serverfix
To git@github.com:schacon/simplegit.git
- [deleted] serverfix
有种方便记忆这条命令的方法:不久前见过 git push [远程名] [本地分支]:[远程分支]
语法,如果省略 [本地分支]
,那就等于是在说“在这里提取空白然后把它变成 [远程分支]
”。
分支的衍合
把一个分支中的修改整合到另一个分支的办法有两种:merge
和 rebase(衍合)
衍合的基本操作
假如两个分支进行到如下阶段
使用 merge 操作整合分支,就会把两个分支的最新快照(C3和C4)以及他们的共同祖先(C2)进行三方合并,产生新的提交对象C5。
还有另外一个选择:就是把在 C3 里产生的变化补丁在 C4 的基础上重新打一遍。在 Git 里,这种操作叫做衍合(rebase)。有了 rebase
命令,就可以把在一个分支里提交的改变移到另一个分支里重放一遍。
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
用当前分支给分支名标识的分支打补丁:git rebase 分支名
它的原理是回到两个分支最近的共同祖先,根据当前分支(也就是要进行衍合的分支 experiment
)后续的历次提交对象(这里只有一个 C3),生成一系列文件补丁,然后以基底分支(也就是主干分支 master
)最后一个提交对象(C4)为新的出发点,逐个应用之前准备好的补丁文件,最后生成一个新的合并提交对象(C3'),从而改写 experiment
的提交历史,使它成为 master
分支的直接下游。
然后回到 master 分支,进行一次快速合并
接下来看一个复杂一点的衍合,但是看过之后你会对衍合有更深的理解。
假如 master 操作后又添加了新的分支 server ,提交到 C4,这时又添加 client 分支修改客户端代码,提交到 C9 又回到 server 并提交到 C10。
接下来在一次发布中要把 client 合并到 master 中。这个时候,我们就可以把 client 分支的改变即 C8 和 C9,跳过 server
直接放到 master
分支中重演一遍,但这需要用 git rebase
的 --onto
选项指定新的基底分支 master
:
$ git rebase --onto master server client
这个命令的意思是:“取出 client
分支,找出 client
分支和 server
分支的共同祖先之后的变化,然后把它们在 master
上重演一遍”。结果如图。
现在可以快进 master 了
$ git checkout master
$ git merge client
现在决定把 server
分支的变化也包含进来。我们可以直接把 server
分支衍合到 master
,而不用手工切换到 server
分支后再执行衍合操作 — git rebase [主分支] [特性分支]
命令(注意:特性分支也就是要使用的改变所在的分支是在命令的最后面,就可以理解 --onto 中最后是 client 了)会先取出特性分支 server
,然后在主分支 master
上重演:
$ git rebase master server
然后就可以快进主干分支 master
了:
$ git checkout master
$ git merge server
现在 client
和 server
分支的变化都已经集成到主干分支来了,可以删掉它们了。
$ git branch -d client
$ git branch -d server
衍合的风险
使用衍合也是有风险的,但是请记住:
一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。
也就是说,如果把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合那些尚未公开的提交对象,就没问题。如果衍合那些已经公开的提交对象,并且已经有人基于这些提交对象开展了后续开发工作的话,就会出现叫人沮丧的麻烦。 因此,最好在分享之前进行衍合。