【git学习笔记(二)】Git分支

前言:这段时间,打算学习一下git的操作,了解一下原理,而不是仅仅局限于一些基础的操作,特在此记录一下。

3.2 Git分支–分支的新建与合并

新建分支
解决追踪系统中的 #53 问题,想要新建一个分支并同时切换到那个分支上,你可以运行一个带有 -b 参数的 git checkout 命令:

$ git checkout -b iss53
Switched to a new branch "iss53"

它是下面两条命令的简写:

$ git branch iss53
$ git checkout iss53

接下来,修复这个紧急问题。 我们来建立一个 hotfix 分支,在该分支上工作直到问题解决:

$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 1fb7853] fixed the broken email address
 1 file changed, 2 insertions(+)

你可以运行你的测试,确保你的修改是正确的,然后将 hotfix 分支合并回你的 master 分支来部署到线上。 你可以使用 git merge 命令来达到上述目的:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除 hotfix 分支,因为你已经不再需要它了 —— master 分支已经指向了同一个位置。 你可以使用带 -d 选项的 git branch 命令来删除分支:

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

现在你可以切换回你正在工作的分支继续你的工作,也就是针对 #53 问题的那个分支(iss53 分支)。

$ git checkout iss53

分支的合并
假设你已经修正了 #53 问题,并且打算将你的工作合并入 master 分支。 为此,你需要合并 iss53 分支到 master 分支,这和之前你合并 hotfix 分支所做的工作差不多。

$ git checkout master
$ git merge iss53

既然你的修改已经合并进来了,就不再需要 iss53 分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。

$ git branch -d iss53

遇到冲突时的分支合并
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 分支的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

任何因包含合并冲突而有待解决的文件,都会已未合并状态标识出来。Git会在有冲突的文件中,加入标准的冲突解决标记,这样,你可以打开这些包含冲突的文件,然后手动解决冲突。出现冲突的文件会包含一些特殊的区段,看起来像下面这个样子:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

这表示 HEAD 所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

<div id="footer">
please contact us at email.support@github.com
</div>

上述的冲突解决方案,仅保留了其中一个分支的修改,并且 <<<<<<< , ======= , 和 >>>>>>> 这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用 git add 命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。

等你退出合并工具之后,Git 会询问刚才的合并是否成功。 如果你回答是,Git 会暂存那些文件以表明冲突已解决: 你可以再次运行 git status 来确认所有的合并冲突都已被解决:

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

    modified:   index.html

如果你对结果感到满意,并且确定之前有冲突的的文件都已经暂存了,这时你可以输入 git commit 来完成合并提交。 默认情况下提交信息看起来像下面这个样子:

Merge branch 'iss53'

Conflicts:
    index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#	.git/MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	modified:   index.html
#

3.3 Git分支–分支管理

分支管理
常用的分支管理工具:
git branch,如果不加任何参数运行它,会得到当前所有分支的一个列表:

$ git branch
  iss53
* master
  testing

查看每一个分支的最后一次提交,可以运行 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

–merged与–no-merged这两个有用的选项,可以过滤掉这个列表中,已经合并或尚未合并到当前分支的分支。
如果要查看哪些分支,已经合并到当前分支,可以运行git branch --merged:

$ git branch --merged
  iss53
* master

查看所有包含未合并工作的分支,可以运行git branch --no-merged:

$ git branch --no-merged
  testing

3.4 Git分支–分支开发工作流

长期分支
许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在 master 分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。 这样,在确保这些已完成的主题分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。

主题分支
主题分支对任何规模的项目都适用。主题分支是一种短期分支,它被用来实现单一特性或其相关工作。

请牢记,当你做这么多操作的时候,这些分支全部都存于本地。当你新建和合并分支的时候,所有这一切都只发生在你本地的Git版本库中----没有雨服务器发生交互。

3.5 Git分支–远程分支

远程分支
远程跟踪分支是远程分支状态的引用。它们是你无法移动的本地引用。一旦,你进行了网络通信,Git就会为你移动它们以精确反映远程仓库的状态。
它们以《remote》 / 《branch》的形式命名。

推送
当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。本地的分支并不会自动与远程仓库同步----你必须显示地推送想要分享的分支。这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支。
如果希望和别人一起在名为 serverfix 的分支上工作,你可以像推送第一个分*样推送它。 运行 git push :

跟踪分支
从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”(它跟踪的分支叫做“上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入git pull,Git能自动地识别去哪个服务器上抓取、合并到哪个分支。

拉取
当git fetch命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。它只会获取数据然后让你自己合并。然而,有一个命令叫作git pull在大多数情况下,它的含义是一个git fetch紧接着一个git merge命令。如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显示地设置,还是通过clone或checkout命令为你创建的,git pull都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据,然后尝试合并入那个远程分支。
由于git pull的魔法,经常令人困惑,所以,通常单独显式地使用fetch与merge命令会更好一些。

删除远程分支
假设你已经通过远程分支做完所有的工作了----也就是说你和你的协作者已经完成了一个特性,并且将其合并到了远程仓库的master分支(或任何其他稳定代码分支)。可以运行带有–delete选项的git push命令来删除一个远程分支。如果想要从服务器上删除serverfix分支,运行下面的命令:

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

3.6 Git分支–变基

变基的基本操作
【git学习笔记(二)】Git分支
可以通过合并操作来整合分叉的历史。
其实,还有一种方法:你可以提取在C4中引入的补丁和修改,然后,在C3的基础上应用一次。在Git中,这种操作就叫做变基(rebase)。你可以使用rebase命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。
在这个例子中,你可以检出experiment分支,然后,将它变基到master分支上:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理,是首先找到这两个分支(即当前分支experiment、变基操作的目标基底分支master)的最近共同祖先C2,然后,对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后,将当前分支指向目标基底C3,最后,以此将之前另存为临时文件的修改依序应用。
【git学习笔记(二)】Git分支
将C4中的修改变基到C3上
现在回到master分支,进行一次快进合并。
【git学习笔记(二)】Git分支
master分支的快进合并
此时,C4’指向的快照就和the merge example中C5指向的快照一模一样了。这两种整合方法的最终结果没有任何区别,但是,变基使得提交历史更加简洁。
无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。变基是将一系列提交按照原有次序,依次应用到另一分支上,而合并是把最终结果合在一起。

变基的风险
如果提交存在于你的仓库之外,而外人可能基于这些提交进行开发,那么不要执行变基。
变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用git rebase命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

变基vs.合并
总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

上一篇:Git简单操作(小白版)


下一篇:git的使用