【Git】Git入门
一、Git相关概念
1. Git与Github、GitLab
1.1 Git定义
-
作用:Git作为目前最流行的分布式版本管理工具
-
作者:Linus(大名鼎鼎的Linux操作系统的作者)
-
优点:在设计理念、性能、安全性,易用性等方面都有着传统的版本管理工具无可比拟的优势。除此之外,它还是完全开源和免费的
-
补充说明:
-
Git和Github不是一个概念,两者有着本质的区别:前者是一个版本管理工具,而后者是一个项目托管平台。在英文中hub的意思是“中心,核心”,所以Github意为它是以Git作为版本管理工具的项目托管平台。
-
作为一个工具来说,Git是不需要联网的。只要在本地安装了Git,就可以使用命令行对本地项目进行版本管理。但如果希望有一个网站可以帮你托管你的项目(这样即使本地数据损坏了,你的代码仍然可以找回来),或者希望把这些代码分享给别人,那么你可以在Github上通过远程仓库来管理它们。
-
1.2 Github定义
- 定位:开源项目托管平台,网站用户可以在这里看到最优秀的项目代码
- 作用:Github倡导开源,希望通过分享促进计算机技术的发展
1.3 GitLab定义
- 定位:管理自己的私人项目或者企业项目,那你就可以使用GitLab
- 作用:GitLab最初就是为了解决github不便于管理私人和企业项目的问题出现的,现在几乎已经成为了企业开发的首选
2. 分布式与集中式版本管理
2.1 何为集中式版本管理
-
代表:传统的项目管理工具,如CVS、SVN等
-
定义:所谓的集中式,就是整个项目都由一台“中央服务器”来管理,每个开发人员需要从“中央服务器”拉取代码进行开发,开发完毕后把代码提交回中央服务器
-
缺点:由于拉取的只是代码本身,不包括提交记录等项目信息,所以一旦服务器中的数据损坏,所有的提交记录都会丢失,会给项目开发带来巨大的损失(图片来自网络)
2.2 何为分布式版本管理
-
代表:Git
-
定义:Git会在每台主机上维护一个完整的版本仓库,并且它不存在“中央服务器”,每台主机都可以认为是一台“服务器”
-
优点:主机之间通过交换修改记录来保证项目进度的统一,Git会根据修改记录快速更新代码。这样即使一台主机的数据损坏,仍然可以从其他主机获取完整的项目数据,这非常有利于多人协同开发(图片来自网络)
- 灵活点:如果各个主机之间无法直接通信,也可以使用一台服务器作为中介,用于交换修改记录。或者直接在这台服务器上构建一个项目仓库,所有的开发者都向该服务器提交修改记录,服务器会保留这些记录,并依据它们来更新代码。然后每台主机就可以从服务器上拉取修改记录,本地的Git就会根据修改记录来更新本地仓库,以此来保证项目进度的统一。而Github和GitLab通常就可以看作这样一台“服务器”,它们为我们托管了一个远程版本的项目仓库。
3. Git原理简介
3.1 版本库
-
定义: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命令,你可以清楚地看到项目的整个迭代过程。
3.2 远程仓库
上面介绍的是在本地使用Git,此时你可以在完全断网的情况下使用Git进行正常的项目管理。但这种模式在多人协作开发的情况下并不适用,因为每个开发者每次进行commit,都必须把修改记录发送给团队其他成员,才能保证项目进度统一,这样效率非常低。
- 定义:搭建一个服务器,在服务器上新建一个远程仓库。团队的每个成员只要保持与这个版本库统一,就可以保证整个团队进度统一。可以借助项目托管平台github或gitlab来做这件事。- 选择:如果你想要开发一个开源项目,那么可以在github上构建一个远程仓库,将它与本地仓库建立联系,通过在本地仓库和远程仓库之间进行代码的拉取(pull)和推送(push)来保持版本统一。如果你想要开发一个私人项目,或者正在参与公司项目,那么可以用GitLab进行版本管理。
- 作用:远程仓库不仅可以作为本地项目的备份(对于个人项目,这可以增强安全性),还可以作为团队开发的管理平台,对项目管理有很大的帮助。
3.3 分支管理
-
问题1:
- 上面说到,Git默认会创建一个master分支来管理项目代码。但是如果项目需要多人合作,往master分支上提交的各个commit就可能发生冲突(比如同时修改了某部分的代码),这样每个人每次提交代码时都必须手动解决冲突之后才能提交上去。更糟糕的是,如果某个开发者在解决冲突时无意间改动了其他开发者提交的代码,整个项目就会变得混乱起来。
- 另一种情况,如果你希望在项目中试验性地开发一个新功能,而这个新功能可能涉及到项目中相当多的文件。为了不影响项目中其他成员的正常开发,你就只能在本地保存你的修改。假如有一天你的本地数据发生了损坏,那么你为这个功能所编写的代码都将付诸东流。
-
解决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分支上产生一个相对稳定的节点,对外发布的时候就作为一个大的版本升级。
-
4. 总结
本文只是对Git原理的简介,并没有涉及到Git的使用。如果想要学习Git的使用,点开文章进行了解学习。