分支管理(第11篇)

分支概述

Git里有一个很神奇的特性叫分支。说到分支,你脑海中的第一印象可能是火影忍者里的影分身术:复制多个本体。事实上,Git里的分支并不是为了复制多个本体,Git分支的存在更像是派一个探险员,走在自己的前面先尝试着做某件事情,或者去解决某个问题,等到这个探险员觉得没问题了,自己再跟上探险员的脚步。

这样的事情在实际开发中经常存在。比如你在开发一个软件,需要增加一项新功能,你绝不能贸然的就去直接添加,因为新功能很可能会跟原本的代码有冲突,最后导致原本的代码也受到了影响。你希望的是,我可以在一个单独的地方做这件事,而不影响到原本的代码,等到这个新功能开发完成,再将你的代码合并到原来的代码中。

在Git中,你可以使用分支来做这件事。

事实上,之前我们所做的所有工作都在master分支上,master分支是主分支。之前我们为了学习Git,一直都在master分支上进行修改。而真实的开发中,我们不会这么做。因为真实的开发过程有很多的不确定性,在master分支上开发会很危险。我们通常都会新创建一个分支叫dev,它是develop的缩写,当然你也可以使用其他名字。我们的开发工作都是在这个dev分支上进行的。dev分支是开发的总分支,而如果你要开发一个新功能,或者修复一个Bug,你又需要在这个dev分支上创建其他的分支。你可能会跟很多人一起开发,每个人都在dev分支上分出一条自己的分支,每个人还可以在自己的分支中再创建子分支,然后当个人分支完成后,根据需要再往dev分支上合并,dev上完成一项任务后还可以往master分支合并。这样每个人都可以在自己的分支上做自己的事情,不会打扰到其他人,也不用担心自己未完成的代码会无意之中破坏master分支中的代码。

创建分支

首先我们创建一个test.txt的文件,在里面输入:

# this is a test file

然后我们将它提交:

$ git add test.txt
$ git commit -m "add a test file"

接着,我们来创建一个dev分支。

$ git branch dev

这样,就生成了一个叫dev的分支。
但别忘了,我们现在仍然在master分支,不信你看:

$ git branch
  dev
* master

git branch会列出所有的分支,并在当前分支的前面加上一个*号。如果需要切换到dev分支,我们只需:

$ git checkout dev

还记得我们在撤销修改那一篇中使用过checkout吗?不过那里的的语法是:git checkout -- file,用于撤销工作区的修改。而切换分支命令没有 -- 这两个短横。

如果你希望创建一个分支并直接切换到那个分支,还可以:

$ git chekcout -b dev

这样将会创建dev分支并直接切换到dev分支。

好了,现在我们已经从master分支切换到了dev分支。master是老大,他现在让dev分支先去探探路,没办法,谁让他是老大呢!
我们在dev分支中进行修改。在test.txt后面加上一行:

# this is a test file
first line

接着我们进行提交:

$ git add test.txt
$ git commit -m "add first line  on dev"

好了,我们在dev分支上给test.txt加上了一行。现在,我们切换回master分支:

$ git checkout master

当我们切换回master分支后,打开test.txt,发现:

$ cat test.txt
# this is a test file

我们刚才添加的一行"first line"不见了。
因为"first line"是在dev分支上提交的。

合并分支

现在,dev作为一个探险者已经出色的完成了它的探险任务,master需要跟它合并了。
现在我们在master分支上,我们准备跟dev分支合并:

$ git merge dev
Updating 0fed6b6..38fb696
Fast-forward
test.txt | 1 +
1 file changed, 1 insertion(+)

合并完成了。
Git告诉我们这次合并是Fast-forward(快速模式)。当然还有其他的模式。

合并完成以后,你还可以删除dev分支:

$ git branch -d dev

如果你乐于尝试,你可能还会做一个实验:

  • 先在master分支下创建一个test.txt文本,并在里面写上
# this is a test file

然后git add ,git commit提交。

  • 再创建一个dev分支,并切换到dev分支:
$ git checkout -b dev
  • 在dev分支下,给test.txt添加一行:
# this is a test file
first line
  • 前三步都跟刚才的一样,这第四步有所区别,我们不进行提交,我们在dev分支上只将test.txt添加到暂存区:
$ git add test.txt

记住,我们不在dev里commit提交。

  • 我们切换到master分支:
$ git checkout master

这时,你打开test.txt发现,你在dev分支上添加的“first line”仍然还在:

# this is a test file
first line

你可能会觉得难以理解:“first line”不是在dev分支上添加的吗?怎么在master分支上还能看见?事实上,即使第4步你没有将test.txt添加到暂存区,仍然会有同样的结果。
那是因为,你虽然是在dev分支上添加的“first line”,但是并没有在dev分支提交“first line”。
现在有两种情况:
第一种情况,如果此时你切换回dev分支,并将“first line”这一修改进行提交,然后再返回到master分支,这时,你就不会再看到“first line”了。
第二种情况,如果你没有切换回dev分支进行提交,而是在master分支进行提交,那么当你返回到dev分支时,你将不会再看到“first line”了,因为Git认为“first line”这一修改是在master分支上进行的,而这一修改也确实是在master分支上提交的。

看到这里,你是不是有点感觉:Git好像只在版本库上进行了分支上的区分,而没有将工作区和暂存区在分支上进行区分。

你暂时可以这样理解。

还有一件事情要注意:一旦两个分支的版本库不一样,这时你在某个分支上进行了修改而没有提交,那么Git是不允许你切换分支的,除非你强制切换。这时你刚刚在这个分支上做的修改也会被覆盖。(当然Git也允许你将当前的状态保存下来,这样不用提交就可以切换分支了。)

合并时发生了冲突

刚才我们在进行合并时很顺利,因为dev分支比master分支提前了一个提交,这样master分支在跟dev合并时只要把自己的指针指向dev就可以,并不会有冲突。

但是如果在dev分支上修改了一些内容并进行了提交,然后返回master分支,也修改了一些内容并进行了提交,这时你再进行合并,就会有冲突。因为两个分支都进行了修改,那最后合并的结果到底听谁的?Git也不知道怎么办。这时需要你自己打开文件,手动的来解决文件中发生的冲突。

  • master分支中有一文件test.txt:
# this is a test file
  • 创建dev分支并切换到dev分支:
$ git checkout -b dev
  • 在dev分支中修改test.txt,添加一行:
# this is a test file
new line on branch dev
  • 在dev分支上提交这一修改:
$ git add test.txt
$ git commit -m "add new line on branch dev"
  • 然后切换到master分支:
$ git checkout master

可以查看在master分支里,“new line on branch dev”这一行并没有添加到test.txt中,因为这一修改是在dev分支提交的。

# this is a test file
  • 在master分支上打开test.txt,添加一行新文本:
# this is a test file
another new line on branch master.
  • 在master分支上进行add 和commit:
$ git add test.txt
$ git commit -m "add another new line on branch master"
  • 然后我们进行合并:
$ git merge dev
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

发现报错了,Git告诉我们发生了冲突。这是我们打开test.txt发现Git已经自动为我们在发生冲突的地方标记了出来:

$ cat test.txt
# this is a test file
<<<<<<< HEAD
another new line on branch master
=======
new line one branch dev
>>>>>>> dev

Git使用<<<<<<< HEAD ======= >>>>>>> dev的方式为我们标记出了冲突的地方,我们将冲突的地方手动进行修改:

# this is a test file
anyway,this is a new line

这时再进行提交:

$ git add test.txt
$ git commit -m "fix conflict"

好了,冲突解决了。master分支上的test.txt已经变成你刚刚修改的那样了,变成了“anyway,this is a new line”。
你可以用带参数的git log看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
 *   3671953 fix conflict
|\
| * 74a391b add new line on branch dev
 * | 8a54ef7 add another new line on branch master
|/
 * 901e873 add a test file
 * 38fb696 add first line on dev
 * 0fed6b6 add a test file
  • 现在我们回到dev分支,你查看一下test.txt会发现:
# this is a test file
new line on branch dev

仍然还是“new line on branch dev”。这是因为合并发生在master分支上,跟dev分支无关。

普通合并分支

我们在上面说过,在不发生冲突的情况下,如果可能,Git会使用Fast forward模式合并。但这种方式合并,“合并”这件事情不会被记录下来, 今后你在使用git log查看提交历史的时候不会知道曾经进行过合并。如果你在以后想知道之前有没有进行合并,那么就需要在合并的时候强制禁止fast forward合并方式,将合并这件事本身也作为一次提交。

$ git merge --no-ff -m "merge with no forward" dev 

这样合并这件事就成为了一次提交,-m参数用于添加这次提交的说明。

保存现场

我们前面说过,如果两个分支的版本库不一样,那么你在一个分支上进行了修改并且没有提交,那你是不能切换到另一个分支的。

但如果我在这个分支上的一个工作还没有完成,不适合提交。但我又要立刻切换到另一分支去修改一个Bug,时间紧迫,我又不能撤销我在当前分支上的修改,又不方便提交,还需要立即切换到另一分支去修复那个紧急的Bug……天哪,我到底该怎么办?

事实上,你可以先把当前分支上的工作现场保存下来,然后你就可以切换到另一分支去修改那个Bug,等修复完Bug以后,你再切换回来,恢复工作现场。

Git提供了这样一个命令来帮助你保存工作现场:

$ git stash

你现在使用git status查看一下工作区的状态,Git会告诉你工作区很干净,没有什么要提交的。那你就放心了。
然后你就可以切换到另一分支了。在另一分支完成了工作,你再切换回这个分支。你需要恢复刚才保存的工作现场,但事实上,你可能保存了好几次工作现场,那么你就可以使用git stash list来查看有哪些工作现场:

$ git stash list
stash@{0}: WIP on dev: 893643d add new line on branch dev

你现在需要恢复这个代号叫做stash@{0}的工作现场。
你有两种方法:
第一种,恢复工作现场,但不删除那个工作现场
你可以使用git stash apply恢复,这个命令不会删除工作现场,当然你也可以使用git stash drop来手动的删除工作现场。

$ git stash apply stash@{0}
$ git stash drop

第二种,在回复工作现场的同时,删除工作现场:

$ git stash pop

多人协作

我们通常是把某一台电脑当做服务器,在这台服务器上安装了Git,然后其他人都把这台服务器作为远程仓库,可以从远程仓库中克隆或者推送。

但现在我们在学习阶段,自己搭建一个服务器有点小题大做。我们在第10篇《GitHub与远程仓库》中介绍的GitHub就可以作为你使用的远程仓库。

当你从远程仓库克隆一份到自己的本地时,事实上Git会把你的本地的master分支和远程仓库的master分支对应起来,而且远程仓库的默认名称都是origin。

查看远程仓库

$ git remote
origin

git remote可以用来查看远程仓库的信息,如果你想查看更详细的信息,可以使用:

$ git remote -v 
origin  git@github.com:qiaoer2017/test_repository.git (fetch)
origin  git@github.com:qiaoer2017/test_repository.git (push)

推送分支

git push命令用于推送分支

$ git push origin master

推送的时候需要指定推送的是哪一个分支。如果要推送到dev分支:

$ git push origin dev

当然你也不一定要把所有的分支都推送。一般master和dev需要实时和远程仓库同步,其他分支视情况而定。

抓取分支

我们很容易就可以克隆远程仓库到本地。

$ git clone git@github.com:qiaoer2017/test_respository.git

默认你只能在本地看到master分支。

$ git branch
*master

如果你需要在dev上开发,你就必须切换到dev分支,但dev分支又是默认不可见,该怎么办呢?
很简单:

$ git checkout -b dev origin/dev

这时你就可以在dev分支上正常工作了。

处理冲突

如果你的朋友在dev上向远程提交了一次修改,他在README.md文件最后添加了一行“Good bye!”,并推送到了远程的dev分支上。

而你也对README.md进行了修改并提交到dev分支,只不过你在文件最后一行添加的和他不一样,你添加的是“Bye bye!”而不是“Good bye!”。这时你再往远程推送的时候会发现失败:

$ git push origin dev
To git@github.com:qiaoer/test_repository.git
 ! [rejected]        dev -> dev (fetch first)
error: failed to push some refs to 'git@github.com:qiaoer2017/test_repository.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

因为你的修改跟你朋友的修改(你朋友的修改已经推送到了远程库)有冲突,你需要把他的提交从远程库pull下来,在本地合并。

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:qiaoer/test_repository
   9cc81b9..878a56a  dev        -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
 
    git pull <remote> <branch>
 
If you wish to set tracking information for this branch you can do so with:
 
    git branch --set-upstream-to=origin/<branch> dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch dev set up to track remote branch dev from origin.

然后再:

$ git pull

然后在本地解决冲突,再提交,再推送。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 1.git的安装 1.1 在Windows上安装Git msysgit是Windows版的Git,从https:/...
    落魂灬阅读 12,663评论 4 54
  • 本文为 Git教程的学习笔记,教程源自廖雪峰的博客。这是一个由浅入深,学完后能立刻上手的Git教程。另,附上另一本...
    七弦桐语阅读 6,247评论 5 47
  • 现在类似于“越努力越幸运”“你若输的起,才会赢得到”诸如此类励志的鸡汤一篇又一篇充斥着朋友圈,直白说,我不反对...
    川城紫风阅读 463评论 0 1
  • -1- 白灰匆匆抹上红砖 时光就老了城墙 土里的藤蔓爱上了墙上的窗 耗尽青春 终于赶上了它的迟暮 -2- 破旧的帆...
    陈贰九阅读 868评论 33 26
  • 这一篇主要是CoreAudio官方文档的重点内容的笔记。 通过回调函数与CoreAudio交互 iOS的CoreA...
    brownfeng阅读 5,074评论 10 7