本文正在参加「金石计划 . 分割6万现金大奖」

文章首发于公众号:程序员读书;欢迎关注,能够第一时间收到文章更新哦!

分支模型是Git的必杀技,也是Git最强壮的地方,掌握Git分支的运用,也便是学到Git的精华。

Git分支的实质

分支的实质便是一个引证(指针),是对提交(commit)的引证

Git会运用SHA-1哈希算法给每次提交生成一个40位个哈希值(commit id),如下所示,这个值是仅有的,只需咱们修正文件再提交,发生的commit id都不一样:

1bcb7ac5ac6444c1fd360345fba553b51021d53b

每次的commit都包含一个指向前一个commit的指针,这些commit连起来便是一条咱们提交的时间线了。

分支便是指向某个commit的指针,不同分叉代表不同的分支走向,不同分支也能够指向同一个commit,如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

Git库房还有个叫HEAD的指针,HEAD指向当时作业区地点的分支,当咱们切换分支时,HEAD就会指向咱们所切换的分支,比如鄙人面的暗示图中,咱们把分支从master切换到developHEAD也随之改动。

写给自己的Git分支使用备忘指南|Git简明教程

分支的根本操作

为了更好地学习Git分支的知识,在这篇文章中,咱们以一个从零开始的项目来详细讲解Git分支的运用。

初始化库房

首要初始化一个空的库房,指令如下:

$ git init demo

创立好项目并初始化库房后,Git默认会帮咱们创立一个名为master,假如你是经过指令行来操作的,应该能够看到相似下面图片的指令行提示符:

写给自己的Git分支使用备忘指南|Git简明教程

创立库房

现在咱们开始在master分支上增加文件,并提交咱们的修正:

# 创立文件
$ echo "Hello Git!" > hello.html
# 增加到暂存区
$ git add hello.html
# 提交修正
$ git commit -m 'new file hello.html'

履行git commit指令后,发生了一个commit记录,master分支指该commit,此刻分支状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

当然,咱们也能够多commit几次,假定咱们再多提交两次,那么此刻master分支指向最新的commit,分支状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

创立分支

Git的分支办理功用指令为:git branch

现在假定咱们要在项目中开发一个新功用,按正常开发流程,咱们需求根据master分支创立一个新的功用分支,在git branch指令后直接跟上分支名即可:

$ git branch feature/1

履行后,咱们就创立了一个名为feature/1的分支。

切换分支

不过,在创立分支后,Git并没有帮咱们切换到feature/1上,切换分支的指令为git checkout:

$ git checkout feature/1

git checkout也能够在分支不存在时,直接创立并切换,只需跟上-b选项即可:

$ git checkout -b feature/1

留意,git checkout指令跟上选项-b的话,假如要切换的分支现已存在的话,则会报错,此刻要去掉-b选项。

切换到feature/1分支之后,新增一个文件并提交:

# 创立文件
$ echo "This is feature 1" > feature1.html
# 增加到暂存区
$ git add feature1.html
# 提交修正
$ git commit -m 'new file feature1.html'

此刻库房各个分支状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

检查分支

直接运转git branch指令能够检查当时库房的分支:

$ git branch
* feature/1
  master

输出成果中,有的分支名前面有*号,表明当时检出的分支,也是HEAD指针指向的分支。

跟上-v选项后,能够检查一切分支的最后一次提交的commit Id和提交时咱们输入的阐明文字:

$ git branch -v
* feature/1 69d618f new file feature1.html
  master    ac02f4c new file hello.html

--merged选项能够检查现已兼并的分支:

$ git branch --merged

--no-merged选项能够检查未兼并的分支:

$ git branch --no-merged

兼并分支

在进行分支兼并时,一般会呈现三种状况:快进兼并(Fast-forward)、正常分支兼并和分支抵触;下面咱们一一评论。

快进兼并

假定现在咱们现已feature/1分支上完结了功用开发,那接下来便是将feature/1分支兼并master上了,Git的分支兼并指令为:git merge

# 切换到master分支
$ git checkout master
# 将feature/1分支兼并到master
$ git merge feature/1
Updating ac02f4c..69d618f
Fast-forward
 feature1.html | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 feature1.html

兼并完结后,能够看到输出的信息有Fast-forward的提示文字,阐明此次兼并形式为:快进兼并。由于feature/1分支是master分支的基础上创立的,两个分支之间没有呈现分叉,因而Git在兼并feature/1master时,只需求将master指向feature/1分支指向的commit id即可,所以称为快进兼并,如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

正常分支兼并

为了更好模拟正常分支兼并的状况,现在让咱们回到feature/1分支未兼并到master时的状况,然后再切到master分支,根据master分支创立feature/2分支,此刻状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

接着再将feature/1分支兼并到master分上(快进兼并),如下图所示,master分支和feature/2分支呈现分叉,呈现不同的走向:

写给自己的Git分支使用备忘指南|Git简明教程

这时将feature/2兼并到masterGit不会再选用快进兼并的办法,而是将feature/2的提交与master的提交整合在一起,并发生一个新的提交,如下图所示:

$ git merge feature/2

写给自己的Git分支使用备忘指南|Git简明教程

处理抵触

假如兼并分支时呈现抵触,处理办法也不一样的,现在咱们假定feature/2分支与feature/1都修正了home.html文件的同一行,且feature/1已兼并到master了,状况与上面的正常分支兼并相似,不同的是feature/1feature/2都改了同一个文件,并发生抵触:

写给自己的Git分支使用备忘指南|Git简明教程

此刻再将feature/2兼并到master,Git会提示咱们兼并发生抵触了,需求手动处理:

$ git checkout master
$ git merge feature/2
Auto-merging hello.html
CONFLICT (content): Merge conflict in hello.html
Automatic merge failed; fix conflicts and then commit the result.

抵触的内容会同时出在文件里,此刻假如咱们用git status检查作业区状况,Git会显现both modified文原本提示该文件被两个分支改过:

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)
Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   hello.html
no changes added to commit (use "git add" and/or "git commit -a")

翻开文件,能够发现抵触的代码已被<<<<<<<和=======隔开了,有HEAD标签表明是当时分支master的修正(是从feature修正后兼并过来的),=======下面的则是咱们要兼并的分支的代码。

Hello Git
<<<<<<< HEAD
This commit is from feature/1
=======
the commit is from feature/2
>>>>>>> feature/2

咱们要自己决定抵触内容的去留,修正之后保存文件再commit即可,发生一个新的commit,此刻master指向最新的commit。

$ git add .
$ git commit -m '兼并抵触文件'

如下图所示,发生抵触后咱们自己手动兼并然后提交,其分支走向与上面的正常兼并状况相似:

写给自己的Git分支使用备忘指南|Git简明教程

删去分支

关于现已兼并到master的分支,能够删去了,删去分支同样用git branch指令,跟上-d选项与分支名即可:

$ git branch -d feature/1

关于还未兼并的分支,运用-d选项无法删去,Git会提示该分支未兼并,假如坚持要强制删去的话,运用-D选项进行强制删去:

$ git branch -D feature/2

检查提交历史

当咱们在库房中屡次提交,创立多个分支后,你可能需求对现在整个库房分支与提交点的联系进行梳理,也便是检查咱们的提交历史:

$ git log

上面指令未跟参数的状况下,会按顺序输出咱们的提交日志,跟上-p--patch选项,能够检查每次提交的差异,后面的数字表明要显现的日志数量:

$ git log -p -3

长途分支

上面的一切操作,都是在本地完结的,库房也是存储在咱们本地,假如团队的其他小伙伴想参加这个项目,该怎么办呢?

最好的办法便是把咱们的库房推送到一个团队内同享的长途库房,具体要怎么做呢?

长途库房设置

首要当然需求有一个长途库房,假定咱们在Github创立一个了库房,其地址为:https://github.com/test/test,Git关于长途库房设置的指令为:git remote

$ git remote origin https://github.com/test/test

这儿咱们将长途库房称号为设置为origin,这个名字并没有什么特别意义,你也能够设置其他称号,并且,一个库房能够设置多个长途库房,所以除了Github,你也能够再设置一个存储在Gitee的库房。

有了长途库房,就能够将本地的提交推送到长途库房了,运用git push指令:

# -u选项用于设置本地的当时分支盯梢的长途分支
$ git push -u origin master 

这样,咱们就将本地master分支同步推送到长途库房的master分支上了,此刻本地及长途库房分支状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

现在咱们将视角切换咱们团队中另一个小伙伴小A这儿,让小A参加到项目中来,由于咱们现已创立了长途库房,所以小A只需求将长途库房克隆到他的电脑能够了:

$ git clone https://github.com/test/test

由于是经过克隆的办法生成的本地库房,所以Git会自动将长途库房命名为origin,此刻在小A的电脑中,其本地与长途分支联系状况如下图所示:

写给自己的Git分支使用备忘指南|Git简明教程

上面的暗示图中,咱们看到在本地库房中有个称号为origin/master的分支名,这个是长途库房分支在本地的引证,其命名规则为长途库房名+长途分支名,由于咱们把长途库房名为origin,因而这个分支完整称号便是origin/master,留意,这个分支咱们无法修正,只能与长途库房的分支进行同步。

根据长途库房的协作过程

好了,既然团队中的其他小伙伴参加项目中来了,那么接下来便是根据同享长途库房进行协作开发了,具体来说便是将团队中的人把自己的提交推送到长途库房,或者同步其他人推送到长途库房的提交。

推送与同步

在Git中,git push指令用于将分支推送到长途分支。

咱们在前面现已运用git push指令了,这个指令的实质是将当时分支的一切commit发给长途库房的指定分支,长途库房收到commit之后,将commit兼并到指定的分支当中去。

git push的用法如下:

# 未指定推送分支,假如没有设置当时本地分支盯梢的长途分支,则无法推送
$ git push
# 跟上-u选项后,会自动设置当时本地分支盯梢该长途分支,之后就能够直接运用`git push`而不需求指定分支了。
$ git push -u origin master

有时分咱们的推送并不一定成功的,这是为什么呢?假如咱们推送的长途分支上有比咱们本地更新的提交,那么此刻就无法推送成功,需求将长途分支的最新提交同步到咱们本地,然后再推送。

Git同步抓取长途分支提交的指令为:git fetch

$ git fetch origin master

调用git fetch之后,仅仅仅仅把长途分支同步到本地长途分支(每个长途分支在咱们本地都有一个引证)罢了,还没兼并到本地的分支,因而还要再调用兼并指令:

$ git merge origin master

假如觉得每次同步都要执两条指令,那么就用git pull指令吧,这个指令相当于同时履行git fetchgit merge

$ git pull origin master

从上面的演示能够看出,当咱们经过git fetch指令把长途分支同步到本地origin/master分支后,接下来的步骤与本地分支兼并相似,便是把origin/master兼并到master罢了,这个兼并的过程一样会呈现快进兼并、正常分支兼并、分支抵触这三种咱们之前现已评论的状况,处理的办法也是一样的。

存储作业区文件(stash)

某个作业日,小A正在本地自己的分支上开发新功用,突然线上呈现一个bug需求立刻修复,小A不得不切换到master分支并同步线上master分支的代码。

但是A由于功用未开发完结,所以并没有将作业目录中的文件提交到功用分支,切换master分支后,功用分支相关的文件还在作业目录里,假如这些文件中跟线上master分支有抵触,那就没办法同步长途库房的master上的代码了。

有什么办法能够切换到master时,不让功用分支的文件呈现master分支的作业目录下,且不必将未完结的代码提交功用分支呢?

git stash就派上用场,git stash指令能够把当时作业的文件暂时存储到某个独立的地方,把作业区清理洁净,避免未完结的修正对暂时变更发生影响:

$ git stash

留意,git stashgit add指令把文件增加暂存区完全不同。

当小A完结了主线上bug的修复后,要再回到功用分支上开发,履行下面的指令就能够把之前存储的文件调出来,持续功用开发:

$ git stash pop

吊销操作

在运用Git的时分,不免需求吊销之前的操作,大概有以下几种状况:

文件回到提交状况

假如咱们本地暂时调试修正了某个已提交的文件,那么这个文件就变成已修正的状况,但咱们并不想提交这个修正,因而调试完结后,就得把文件恢复到改动之前,git restore指令就派上用场了:

$ git restore 文件名
从暂存区撤回文件

咱们知道git add指令能够帮咱们把修正的文件增加到暂存区,假如发现增加错了,怎么办?同样是运用git restore指令,但需求跟上--staged选顶:

git restore --staged 文件名
修正提交

假如暂存区的文件现已提交了,却发现有代码写错了或者对提交阐明不满意,想修正,但又不再提交,而是想要修正刚刚的提交,能够吗?

能够,把要改的文件改好之前增加到暂存区,然后重新履行git commit,但要跟上--amind选项,表明要修正上一个提交:

$ git commit --amend 
本地回退到某个版本

假如咱们不只仅仅仅修正刚刚的提交,而是要扔掉整个提交,回到上个提次,也是能够做到的,指令如下:

$ git reset --hard HEAD^

HEAD表明指向当时分支最新的commit,HEAD^则表明相关于当时commit的上一个commit:

git reset指令不只能够回到上一个commit,实际上,这个指令后面参数为commit id,因而能够运用这个指令回到任意一个commit

$ git reset --hard a23s93

所以,假如咱们回到之前的提交点后,假如知道记住了后面的提交点的哈希值,也能够调用git reset指令回到那个点。

长途库房回退

假如某个commit现已被咱们push到长途库房了,但却发现有代码写错了,想吊销,能够做到吗?这要分两种状况来分析:

第一种状况:

假如是你一个人保护的库房或者团队中没人根据你推送的分支上开发,也便是说你的吊销推送到这个分支的操作不会影响其他人,那么在本地回退到某个commit之后,再履行git push --force 强制推送到长途库房即可。

$ git reset --hard HEAD^
# -f 或 --force
$ git push --force

第二种状况:

假如你要回退的分支上现已有团队中其他人的最新提交,那千万不要在本地回滚后强制推送,由于很可能把其他人的提交给覆盖掉,这时分就只能正常修正,然后再提交版本了。

小结

洋洋洒洒,4000多字的长文,总结下来就以下三个方面:

  1. Git分支的实质是库房一切提交串起来的时间线中某个commit的引证,便是指向commit的一个指针,这也是Git分支办理高效的原因吧,便是移动指针罢了。
  2. 在本地库房中,知道了如何办理分支,包含分支的创立、删去、兼并、抵触处理、检查提交日志等知识。
  3. 当本地库房与长途库房关联,这时分由于是团队协作,状况可能要复杂一些,咱们要知道如何推送,同步其他人提交的代码,以及如何吊销自己的误操作。