一,序言
之前在工作中也接触过git,但是没有在现公司这里用的功能这么多。主要的差异体现在如下两个方面:
1,之前公司使用git主要基于图形用户界面,这对于处理冲突非常有帮助,不太容易改错地方;而现公司这里git的使用主要基于命令行
2,之前公司使用git的操作相对来说比较简单,没有现公司这里这么多稍微高级一点的功能,比如多个提交合并为一个单独的提交,cherry-pick之类的
3,之前公司的项目开发流程也和现公司有点不一样,之前公司很少有版本的概念,可能是因为互联网公司发布节奏比较快,每次发布都是直接把master分支做发布,重新拉分支也都是基于master分支来拉,所以版本的概念比较弱化,但是来了现公司这里发现版本在里面占了一个很大的比重
所以我还是有必要系统性地学习一下git的,主要是命令行的方式。
二,git简介
git是一个分布式版本控制系统,这里的分布式主要体现在每台服务器上都拥有一个版本库的完整的所有的信息,包括历史信息;所以在git中,理论上没有一个中央服务器的概念,所有的服务器都拥有完整的信息。但是在实际工作中,我们还是会选择一个中央服务器来方便各个版本库信息的交互。不过我们不用担心中央服务器宕机,只要每个服务器不全部宕机就可以。
三,准备git环境
怎么在服务器上安装git,这个过程不在这里展开说明,假定个人的本机电脑上已经安装了git。
3.1,创建git版本库
新建一个目录,cd进入新建的目录,在当前目录下运行指令git init,此时当前目录就被git版本库管理起来了
此时就创建了一个git版本库。新建的目录就叫做工作目录,在工作目录下面有一个名为.git的隐藏目录,这个隐藏目录就是git的版本库。所有被git管理的文件都在这个版本库中。版本库简而言之就是一种具有特殊索引的数据结构,工作目录下面的所有文件都会被该索引管理起来。
按照约定,git版本库位于工作目录下面,我们对源文件的所有修改都是在工作目录下面进行的,然后通过指令被git版本库管理起来。
3.2,首次提交
创建好了版本库之后,接下来就要修改文件并纳入git版本库管理之中了。首先介绍一下git版本库的大概结构:
整个git的结构可以再次细化为三个区域:工作目录(working directory)、暂存区(stage area)、版本库(.git repository)。我们要将工作目录中的修改提交到版本库中(纳入到版本库的管理体系中),需要经过两步:将修改先放到暂存区中,再将暂存区中的修改提交到版本库中。
将修改放到暂存区中这一步骤可以理解为将要提交的文件放到一个地方统一管理起来,以便一并提交。不在暂存区中的文件是不能参与提交的。真正的提交动作是将暂存区中的修改提交到版本库中。
将工作区中的修改暂存起来的指令是git add xxx
将暂存区中的修改提交到版本库中的指令是git commit -m "comment"
git add 后面可以跟目录路径(该目录下面的所有文件都要被暂存),可以跟文件名(或者多个文件名),后面的所有文件名都要被暂存
git commit -m "xxx"中必须有备注,否则不允许提交
3.3,检查状态
检查git版本库的状态可以用git status指令
git status指令会显示出自上次提交以来在暂存区中还没有被提交的修改,在工作目录中还没有被暂存起来的文件(新建的文件),在工作目录中也在暂存区中的文件但是对该文件的修改还没有被暂存;已经被提交的修改不会被显示在git status指令的输出中。
出现上述三种不同状态的原因是:
如果我们在当前工作目录中新建一个文件,那么该文件肯定没有被暂存,就出现了第三种状态。
如果我们对已有文件进行了修改,并暂存起来,那么就会出现第一种状态。
如果我们对已有文件进行了修改。并暂存起来,但是没有提交;同时对该文件又进行了修改但是没有暂存,此时就会出现第二种状态。
可以通过git add ./指令对三面三种状态同时进行暂存,并通过git commit统一提交。
3.4,提交修改
通过git commit -m "comment"将暂存区中的修改提交到git版本库,每次提交都会产生一个唯一的hash值
3.5,显示提交历史
git log命令可以显示出该版本库中历史上所有的修改,但是为了方便显示,git log命令也提供了很多其他选项
git log命令会将所有的提交按照时间顺序降序排列出来,会显示出很详细的提交信息。
git log --oneline选项只显示出每次提交的hash值和提交注释
git log --graph选项会以图形化的方式显示出每次提交,如果有多人同时修改一个文件,图形化界面会显示出交叉提交
3.6,克隆版本库
在git中不同的开发者位于不同的服务器上面,每个开发者都有一个他自己的版本库,开发者们通过交换各自版本库中的提交来实现项目合作。
假定有一个新的开发者也位于这台主机上,他需要在另一个工作目录中编写代码。我们为他创建一个新的工作目录:
mkdir ../first-steps-clone,cd ../first-steps-clone
在当前目录(../first-steps-clone)下面执行git clone ../first-step ./
这样就可以拥有上次源版本库的一个完整的副本,git会自动在当前目录下面创建一个新的版本库,该新的版本库完全复制原来的版本库。
从此也可以看出,git clone指令大致具有如下形式:git clone src dest
git clone不仅仅可以从远端服务器通过SSH协议克隆版本库到本地,也可以在本地不同的目录之间克隆。
3.7,从另一个版本库中获得修改
现在我们已经有了两个版本库(first-step和first-step-clone)了,此时我们可以模拟多人协作的过程了。在first-step中对文件做各种修改然后提交。在first-step-clone中通过git pull指令将first-step中的修改fetch、merge、commit到本地版本库中
由于在clone的时候first-step中的路径已经被存储到了first-step-clone中了,所以可以直接使用git pull指令从first-step中取回新的提交修改。
git pull指令做了如下几件事情:
1,fetch; 将first-step中的修改抓取到本地
2,compare 将first-step中的修改和本地版本库的修改进行比较
3,conflict 如果有冲突,git就不能继续进行下去,需要人工解决冲突后自行提交
4,merge 如果没有冲突,git会自动执行合并操作
5,commit 如果没有冲突,git会自动产生一个提交,并在终端打开一个文件供你写入提交备注
可以看到,如果没有冲突,那么git会自己执行merge , commit所有操作,并在commit的时候弹出一个文件让你写入备注,然后自动创建一个提交;如果有冲突的话,git就停止进行,让你在本地解决了冲突之后由你手工提交
3.8,rm , git rm , git rm --cached
之前对git rm指令不太熟悉,这里一并介绍一下。
rm指令大家都很熟悉,就是将文件删除。假如执行如下的指令,rm foo.txt
对于git来说表示的意思是:本地文件系统中已经将foo.txt删除,但是git版本库中依然没有删除。执行git status会显示出如下所示:
Changes not staged for commit:
deleted: wangyuzhong
也就是对工作目录中文件的修改没有被暂存起来,需要执行git add ./将该删除暂存起来,此时git status会显示:
Changes to be commited:
deleted: wangyuzhong
也就是该删除已经被暂存起来,还没有被提交,可以执行git commit将删除提交
git rm指令是将文件从工作目录和暂存区中同时删除,但是还没有将删除提交,比如执行git rm foov1.txt
git status显示如下:
Changes to be commited:
deleted: foov1.txt
也就是说git rm foov1.txt等价于 rm foov1.txt git add foov1.txt
git rm --cached foov2.txt指令相当于将foov2.txt从暂存区中删除,但是不从本地文件系统中删除,执行git status后显示如下:
Changes to be commited:
deleted: foov2.txt
Untracked files:
foov2.txt
此时我们可以选择git commit将删除提交,也可以选择git add ./恢复删除,并提交