分布式版本控制系统(git基础)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式版本控制系统(git基础)相关的知识,希望对你有一定的参考价值。

一,了解git

1,git是什么?

Git是目前世界上最先进的分布式版本控制系统(没有之一),由Linus公司(创建了开源的linux)开发而成。

2,分布式版本控制系统是什么意思?具体表现在哪?

Git就是分布式管理系统,于其对应的集中式版本控制系统有SVN,简单的说,分布式的版本控制就是每个人都可以创建一个独立的代码仓库,用于管理,各种版本控制的操作都可以在本地完成,每个人修改的代码都可以合并推送到另一个代码仓库中。
而像SVN这样,只有一个中央服务器,所有的开发人员都必须依赖与这个代码仓库,每次版本控制的操作也必须连接到服务器才能完成,很多公司喜欢用集中式的版本控制是为了更好的控制代码,如果个人开发,一般选择git这种分布式系统。

3,git的作用?

举个例子:如果你使用word文件编写一个东西的时候,肯定有这样一个经历,想要删除一个段落,但是想要恢复删除的段落,又怕找不回来了,这时候你可能会将这个文件另存一份,然后接着改,改到一定程度,又接着改,如果一直这样下去,可能你满桌面都是个word文档的修改版,等过了一周你想要找回被是删除的文字,但是已经记不清楚删除前保存在哪个文件里面了,只好一个一个去找,这就比较麻烦了。
于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里看一眼就可以了,岂不是很方便?
这个软件看起来应该像这样子,能记录每次文件的改动:
技术图片

4,集中式和分布式?

Linus一直痛恨的CVS及SVN都是集中式的版本控制系统,而git是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢?
集中式版本控制系统:
技术图片
版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
集中式版本控制系统的缺点:
集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,那肯定是无法接受的。

分布式版本控制系统:
技术图片
分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比如说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之前只需把各自的修改推送给对方,就可互相看到对方的修改了。

和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有一个完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,那么所有员工都无法工作了。

在实际使用分布式版本控制系统的时候,其实很少在两个人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根就没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它也一样干活,只是交换修改不方便而已。
当然,Git的优势不单是不必联网这个简单,后面我们还会看到git极其强大的分支管理,把SVN等集中式控制系统远远抛在了后边。

总结:
集中式版本控制系统(CVS,SVN等):有一个中央服务器,需要在联网的的情况下才能使用,上传速度慢,如果中央服务器挂掉,将无法工作。
分布式版本控制系统(git):无须中央服务器,每个人的电脑上都有一个版本库,安全性高,上传速度快,并且具有强大的分支管理。

二,安装并使用git

1,安装git
最早git是在linux上开发的,很长一段时间内,git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上,现在,git可以在Linux,Unix,Mac和Windows这几大平台上正常运行。

[jonson@localhost ~]$ sudo yum -y install git
安装完成后,还需要最后一步配置,在命令行输入:
[jonson@localhost ~]$ git config --global user.name "jonson"
[jonson@localhost ~]$ git config --global user.email "xxxxxxx.163.com"

因为git是分布式版本控制系统,所以,每个机器都必须自报家门:“你的名字和Email地址”。
注意:git config命令的--global参数,用来和这个参数,表示你这台机器上所有的git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

2,创建版本库
什么是版本库呢?版本库又名仓库(repository),你可以简单理解成一个目录,这个目录里面的所有文件都可以被git管理起来,每个文件的修改,删除,git都能跟踪,以便任何时候都可以追踪历史,或者在将来某个时刻可以“还原”。

#首先,创建一个目录用作版本库:
[jonson@localhost ~]$ mkdir mygit
[jonson@localhost ~]$ cd mygit/
#将目录变成git管理的仓库(初始化git仓库)
[jonson@localhost mygit]$ git init
Initialized empty Git repository in /home/jonson/mygit/.git/
[jonson@localhost mygit]$ ls -a
.  ..  .git

3,将文件添加到版本库

#创建一个名为“test”的文件
[jonson@localhost mygit]$ echo "test1 git" >  test.txt
[jonson@localhost mygit]$ cat test.txt 
test1 git
// 注意:文件一定要放在当前目录下(子目录也行),因为这个一个git仓库,否则将无法找到该文件。
把一个文件存放到git仓库需要两步
#第一步,用命令`git add`告诉git,把文件添加到仓库:
[jonson@localhost mygit]$ git add test.txt 
#第二步,用命令`git commit `告诉git,把文件提交到仓库:
[jonson@localhost mygit]$ git commit -m "this is first version"
[master (root-commit) 504e5b2] this is first version   
 1 file changed, 1 insertion(+)  #提示一个文件被改动,并且插入了一行内容 
 create mode 100644 test.txt

参数解释:git commit 命令,-m后面输入的是本次提交的说明,可以自定义有特点的内容,这样就能从历史记录里方便地找到改动记录。

小结:

git init:初始化一个git仓库
git add <filename>: 添加文件到git仓库
git commit -m <自定义内容>: 提交文件到git仓库

4,时光穿梭机(代码库回滚)

#我们继续修改test.txt文件,改成如下内容:
[jonson@localhost mygit]$ echo "test2 git" >> test.txt
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git

#查看git版本状态,运行git status命令看看结果:
技术图片
git status命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,test.txt被修改过,但还没有准备提交的修改。
虽然git告诉我们该文件被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经记不清上次怎么修改的test.txt,所以,需要用git diff这个命令看看:
技术图片
git diff 顾名思义即使查看difference(差异对比),显示的格式正是Unix通用的diff格式,可以从上面的命令输出看到,我们在最后一行添加了“test2 git”内容。

知道了对test.txt做了什么修改后,接下来我们就需要把它提交到仓库了,提交修改和提交新文件是一样的两步:

[jonson@localhost mygit]$ git add test.txt 
[jonson@localhost mygit]$ git commit -m "this is second version"
[master 2d19893] this is second version
 1 file changed, 1 insertion(+)
#提交后,我们再用git status命令看看仓库对的当前状态:
[jonson@localhost mygit]$ git status
# On branch master
nothing to commit, working directory clean

git告诉我们当前没有需要提交的修改,而且,工作目录是干净的(working directory clean)。

小结:

git status:查看工作区的状态
git diff:查看修改的内容

4.1,版本回退

#先再次修改文件的内容:
[jonson@localhost mygit]$ echo "test3 git" >> test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
#提交修改到仓库:
[jonson@localhost mygit]$ git add test.txt 
[jonson@localhost mygit]$ git commit -m "this is three version"
[master 31acb21] this is three version
 1 file changed, 1 insertion(+)

像这样,你不断对文件进行修改,然后不断提交到到版本库里。不用担心数据的找不到或者丢失的问题,因为每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在git中被称为“commit”,一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,不会造成数据丢失。
在实际工作中,我们肯定记不得每次修改的内容,所以我们可以用git log命令来查看历史记录:
技术图片
git log命令显示了最近到最远的提交日志,我们可以看到3次提交。
如果嫌输出信息太多,可以加上--pretty=oneline参数:
技术图片
可以看到第一段是一大串commit id(版本号),和SVN不一样,git的commit id不是1,2,3...递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示。为什么commit id需要用这么一大串数字表示呢?因为git是分布式的版本控制系统,后面我们还会研究多人在同一个版本库里工作,如果大家都用1,2,3....作为版本号,那肯定就冲突了。

#进行版本回退:
首先,git必须知道当前版本是哪个版本,在git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上个版本就是HEAD^^,如果往上100或者1000个版本可以用HEAD~100表示。

#现在我们把当前版本回退到上一个版本,使用git reset命令:
[jonson@localhost mygit]$ git reset --hard HEAD^
HEAD is now at 2d19893 this is second version
#查看文件内容,可以看到回退到了上一个版本:
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git

技术图片
查看日志,发现最新的那个版本已经看不到了,如果你又想恢复到刚刚的版本,那么还有办法将其还原吗?办法其实还是有的,前提是你必须要找到那个最新版本的commit id,git提供了一个命令git reflog用来记录你的每一次命令:
技术图片

[jonson@localhost mygit]$ git reset --hard 31acb21
HEAD is now at 31acb21 this is three version
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git

现在,你又回到了刚刚的版本环境。

小结:

#版本回退
git reset  --hard HEAD [^|^^|~xx]  
git reset --hard <commit id>
#查看提交历史:
git log 或者 git log --pretty=oneline 
#查看命令历史:
git reflog

5,工作区和暂存区
Git和其他版本控制系统(如SVN)的一个不同之处就是有暂存区的概念。
工作区(Working Directory):就是你在电脑能看到的目录,比如我mygit目录就是一个工作区。

版本库(Repository):git版本库存放了很多东西,其中最重要的就是stage(或者叫index)的暂存区,还有git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
技术图片
我们知道把文件往git版本库里添加的时候,是分两步执行的:
第一步用git add把文件添加进去,实际上就是把文件修改添加到暂存区。
第二步用git commit 提交更改,实际上就是把暂存区的所有内容提交到当前分支。
这里就不做实例了,大家可以从上边的例子就可以弄明白暂存区的概念了,弄明白了暂存区,就明白了git的很多的操作到底干什么。

6,管理修改
接下来我们要了解的是,为什么git比其他版本控制系统设计得优秀,因为git跟踪并管理的是修改,而非文件。
什么是修改?比如你新增了一行,这就是修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,创建了新文件也算一个修改。
为什么说git 管理的是修改,而不是文件呢?我们通过下边的例子的来理解。

1)对test.txt做一个修改,比如加一行内容:

[jonson@localhost mygit]$ echo "test4 git" >> test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test4 git

然后,将文件添加到暂存区:

[jonson@localhost mygit]$ git add test.txt 
[jonson@localhost mygit]$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   test.txt
#

然后,再修改test.txt:

[jonson@localhost mygit]$ sed -i ‘s/test4/test5/‘ test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test5 git

修改完,直接进行提交:

[jonson@localhost mygit]$ git commit -m "add new git"
[master 76e49ac] add new git
 1 file changed, 1 insertion(+)

提交后,在看看状态:
技术图片
可以发现,刚刚第二次的修改竟然没有被提交?
让我们回顾一下操作过程:
第一次修改-->git add--> 第二次修改--> git commit
你看,我们前面讲了,git管理的是修改,当你使用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit 只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

提交后,用git diff HEAD -- test.txt命令可以查看工作区和版本库里面最新版本的区别:
技术图片
可见,第二次的修改确实没有被提交。
那么怎么提交第二次的修改呢?我们可以继续执行git add再执行git commit就可以了,所以一般情况下,我们先别着急提交第一次的修改,先git add 第二次修改,再git commit,就相当于把两次修改合并到一块提交了:
第一次修改 -> git add -> 第二次修改 -> git add -> git commit

7,撤销修改
在工作的过程,难免会有修改错误的地方,比如现在凌晨两点,你在赶一份报告,在test.txt文件中添加了一行:

[jonson@localhost mygit]$ echo "test6 git" >> test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test5 git
test6 git

在准备git add 提交之前,你突然发现,你添加的内容有bug,如果提交可能遭到老板的批评。既然错误发现得很及时,就可以很容易
地纠正它。你可以删掉最后一行,或者手动把文件恢复到上一个版本的状态,如果用git status查看一下:
技术图片
你可以发现,git会告诉你,git checkout -- file 可以丢弃工作区的修改:
[jonson@localhost mygit]$ git checkout -- test.txt
以上执行的命令就是把test.txt 文件在工作区的修改全部撤销,这里有两种情况:
一种是test.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态。
一种是test.txt已经添加到暂存区后,又做了修改,现在,撤销修改就回到添加到暂存区后的状态。

撤销修改后,看看当前test.txt文件的内容:

[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test5 git

文件内容果然复原了。
注意: git checkout -- file 命令中的-- 很重要,没有-- ,就变成了“切换到另一个分支” 的命令。

现在假定是凌晨3点,你突然发现你添加的内容有bug,还git add到暂存区了:

[jonson@localhost mygit]$ echo "new test ha ha" >> test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test5 git
new test ha ha
[jonson@localhost mygit]$ git add test.txt 

庆幸的是,在commit之前,你发现了这个问题。用git status查看一下,修改只是添加到了暂存区,还没有提交:
技术图片
并且git还告诉我们,用命令 “git reset HEAD <file>” 可以把暂存区的修改撤销掉(unstage),重新放回工作区:

[jonson@localhost mygit]$ git reset HEAD test.txt 
Unstaged changes after reset:
M   test.txt

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

再用git status查看一下,现在暂存区是干净的,工作区有修改:
技术图片
所以接下来,就可以用之前丢弃工作区的修改来恢复:

[jonson@localhost mygit]$ git checkout -- test.txt 
[jonson@localhost mygit]$ cat test.txt 
test1 git
test2 git
test3 git
test5 git

可以,看到又恢复到了工作区之前的状态了。

小结:

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令“git checkout -- <file>”

场景2:当你不但该乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令“git reset HEAD <file>”,就回到了场景1,第二步按场景1操作。

8,删除文件
在git中,删除也是一个修改操作,接下来我们实践一下。

#先添加一个新文件test.txt到git并且提交:
[jonson@localhost mygit]$ git add test.txt 
[jonson@localhost mygit]$ git commit -m "add last git"
# On branch master
nothing to commit, working directory clean  #提示工作区是干净的

一般情况下,我们直接通过rm命令将文件删除即可:
[jonson@localhost mygit]$ rm test.txt
这个时候,git知道你删除了文件,因此,工作区和版本库就不一致了,git status 命令会立刻告诉你哪些文件被删除了:
技术图片

现在你有两个选择,一是确定了要删除该文件(不是误删除),那就用命令git rm删掉,并且git commit:

[jonson@localhost mygit]$ git rm test.txt
rm ‘test.txt‘
[jonson@localhost mygit]$ git commit -m "remove git"
[master 8392779] remove git
 1 file changed, 4 deletions(-)
 delete mode 100644 test.txt
[jonson@localhost mygit]$ git status
# On branch master
nothing to commit, working directory clean

现在,文件就从版本库中被删除了。

另一种情况是由于自己的误删除,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版,执行以下命令:
git checkout -- test.txt
git checkout其实是用版本库里的版本来替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

小结:
命令 git rm 用于删除一个文件。如果一个文件已经被提交到版本库,那么可以不用担心误删,但是要小心,你只能恢复文件到最新版本,就会丢失最近一次提交后你修改的内容。

以上是关于分布式版本控制系统(git基础)的主要内容,如果未能解决你的问题,请参考以下文章

Git基础学习(黑马程序员笔记)

Git基础

Git基础与Github

Git --- 基础

git基础知识汇总

g4e基础篇#5 创建分支和保存代码