作用:Git作为目前最流行的分布式版本管理工具
作者:Linus(大名鼎鼎的Linux操作系统的作者)
优点:在设计理念、性能、安全性,易用性等方面都有着传统的版本管理工具无可比拟的优势。除此之外,它还是完全开源和免费的
补充说明:
Git和Github不是一个概念,两者有着本质的区别:前者是一个版本管理工具,而后者是一个项目托管平台。在英文中hub的意思是“中心,核心”,所以Github意为它是以Git作为版本管理工具的项目托管平台。
作为一个工具来说,Git是不需要联网的。只要在本地安装了Git,就可以使用命令行对本地项目进行版本管理。但如果希望有一个网站可以帮你托管你的项目(这样即使本地数据损坏了,你的代码仍然可以找回来),或者希望把这些代码分享给别人,那么你可以在Github上通过远程仓库来管理它们。
代表:传统的项目管理工具,如CVS、SVN等
定义:所谓的集中式,就是整个项目都由一台“中央服务器”来管理,每个开发人员需要从“中央服务器”拉取代码进行开发,开发完毕后把代码提交回中央服务器
缺点:由于拉取的只是代码本身,不包括提交记录等项目信息,所以一旦服务器中的数据损坏,所有的提交记录都会丢失,会给项目开发带来巨大的损失(图片来自网络)
代表:Git
定义:Git会在每台主机上维护一个完整的版本仓库,并且它不存在“中央服务器”,每台主机都可以认为是一台“服务器”
优点:主机之间通过交换修改记录来保证项目进度的统一,Git会根据修改记录快速更新代码。这样即使一台主机的数据损坏,仍然可以从其他主机获取完整的项目数据,这非常有利于多人协同开发(图片来自网络)
定义:Git中一个非常重要的概念叫repository,中文称为版本库,是Git管理项目所使用的项目仓库
作用:通过git命令,你可以向向Git提交代码,或者查看对该项目进行过的所有修改,也可以快速地回到某次修改前的状态。如果是协同开发,还可以查到其他开发者的提交记录
创建:安装Git后,打开命令行。找一个合适的目录,新建一个空文件夹(请尽量避免路径中出现中文)。然后在命令行中进入该文件夹,输入git init
,如图:
一个Git版本库就初始化完成了。现在该文件夹内就会出现一个.git文件夹,如图:
使用:这个.git文件夹就是Git的版本仓库,里面包含了Git管理项目所需要的所有文件,之后所提交的所有修改也都会被记录在该文件夹内的特定文件内。
说明:此时Git的结构如下(图片来自网络):
图中左侧的工作区(Working Directory),它是我们进行代码开发的目录。如果是实际的项目开发,我们通常会在IDE(如eclipse、webstorm、idea等)中操作该目录下的文件。而.git文件夹就是图中右侧的版本库,它主要包含一个暂存区(图中的stage)和一个默认的master分支(图中的master)。
暂存区用于记录文件修改。当使用git add <name>
这样的命令向Git提交代码时,Git就会把该文件中发生的变化记录在暂存区(注意,Git只记录修改的地方,比如某行新增了一个单词,或删除了某行等,这样可以加快更新速度)。
在命令行中输入git commit -m "xxx"
可以把暂存区中的修改记录提交到主干分支,Git会根据修改记录来更新master分支的代码。-m
后面的内容是关于本次提交的描述信息,你可以在这里记录修改了哪些内容,方便以后查看和回退。提交了修改之后,工作区的项目代码就和master分支的代码一致了。
每次执行commit都会在master分支上生成一个新的节点,用于记录本次commit后该分支的状态。当以后查看和回退代码的时候,就在master分支上搜索节点的ID。
Git是使用C语言编写的,它使用一个名为HEAD的指针来指向当前分支中最新的节点,用另一个名为master的指针指向master分支中最新的节点(之所以使用两个指针,是因为Git存在分支管理,当你切换到其他分支时,HEAD会指向那个分支中最新的节点,后面会详细介绍),每次的commit产生的节点连起来就构成了我们的master分支。使用git status
命令可以查看当前分支所有的commit,这就是你对该分支的所有修改记录。所以如果某次commit出现了问题,只需要根据你所写的描述信息找到这个commit,然后用git reset
命令回退到该commit之前的状态,就可以轻松回退代码。对于Git而言,只是把指针指向之前的某个节点,因此操作速度非常快。使用Git命令,你可以清楚地看到项目的整个迭代过程。
上面介绍的是在本地使用Git,此时你可以在完全断网的情况下使用Git进行正常的项目管理。但这种模式在多人协作开发的情况下并不适用,因为每个开发者每次进行commit,都必须把修改记录发送给团队其他成员,才能保证项目进度统一,这样效率非常低。
问题1:
解决1:Git使用分支策略来解决上面的问题,如图:
我们之前讲到,master分支是由一个个commit构成的链式结构,每次提交commit,这个结构就会长一点。Git会使用一个专门的指针master指向master分支的最新节点。如果我们现在通过git branch
命令创建并切换到一个名为dev的分支,Git就会创建一个新的指针dev,并将master指针锁定在之前的位置(图中蓝色箭头指向的位置)。之后你在dev分支上所做的修改都会沿着之前的节点继续产生新的节点,但是master指针并不会移动,只有dev指针和HEAD指针在移动(从这里可以看出,HEAD指针永远指向当前节点位置,无论你切换到哪个分支)。
也就是说现在master分支的代码停留在了切换分支时的位置,你在dev分支上所做的修改都会沿着切换分支的节点产生一条新的链式结构。如果你现在切换回master分支,Git会直接把HEAD指针指到master指针所在的位置,于是你可以从图中蓝色箭头的位置继续master分支的开发。这样你在dev分支上所做的任何修改都不会影响到master分支。
下图可以更容易帮你理解分支切换的过程:
如图,每个分支都有一个专门的指针来指向最新的节点,你切换到哪个分支,Git就会把HEAD指针切向哪里(如切换到master分支时,Git直接把master指针赋值给HEAD),之后你所提交的commit就会在这个分支上产生新的节点。
问题2:随着分支越来越多,我们如何把各个分支上的功能整合到一起呢?因为最终我们需要打包上线的是master分支中的代码,所以这个问题就是如何把创建出来的这些分支归并到master分支上。
解决2:
通常情况下,作为团队开发的一员,我们没有向master分支提交代码的权限。管理员会给每个团队成员开放developer(开发者)权限,它允许我们从master上创建自己的分支,然后在自己的这个分支上进行开发。当我们开发完毕后,就需要向管理员提交一个归并请求(merge request),表示希望将当前的分支归并到master分支上去。管理员接收到这个请求,就会把当前分支和master分支的最新节点进行合并,在master上产生一个新的节点,这样我们在分支上所做的修改就纳入到master分支上了。
不过如果master产生的分支很多,在向master提交归并请求的时候就可能产生冲突(比如两个人都修改了某个文件,但是他的分支比你更早地归并到了master上,这个文件就可能发生冲突),这时候就需要管理员手动解决冲突。因为Git记录修改非常精确,所以在文件发生冲突时,它可以告诉管理员具体是哪些行发生了冲突,并且使用特殊的标记将这些冲突行标注出来。管理员手动解决冲突后,就可以把代码合并到master分支上。这个时候,如果当前分支所做的工作已经全部完成,就可以清理掉该分支了。如果以后有新的功能要写,可以重新从master上创建一个分支,进行新的功能开发,然后按同样的策略进行分支合并。
有人可能觉得,有了分支之后不是仍然需要解决冲突吗?确实,多人协同开发时冲突是无法避免的。不过使用了分支策略后,我们解决冲突的次数大大减少,因为提交归并请求的次数远远少于提交commit的次数。在实际的项目开发中,Git分支经常看起来是这样的:
master可以看做是实际的产品线,每个开发者都基于这个产品线独立地开发功能,然后不停地归并代码,直到最终项目开发完成。master分支上的每一个节点通常都对应正式产品一个小的版本。如果进行了一次大规模的归并,就会在master分支上产生一个相对稳定的节点,对外发布的时候就作为一个大的版本升级。
本文只是对Git原理的简介,并没有涉及到Git的使用。如果想要学习Git的使用,点开文章进行了解学习。