关于版本控制

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。你可以对任何类型的文件进行版本控制。

如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本,采用版本控制系统(Version Control System,简称VCS)是个明智的选择。 有了它你就可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。 使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。

本地版本控制系统

许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。

为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。

浮图秀图片_git-scm.com_20211107233954.png

集中化版本控制系统

接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。这类系统,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 多年以来,这已成为版本控制系统的标准做法。

浮图秀图片_git-scm.com_20211107234306.png

现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。

这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

分布式版本控制系统

于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

浮图秀图片_git-scm.com_20211107235120.png

许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。也就是我们后边要提到的使用Git进行合作。

Git简史

很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。

Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?

事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!

你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。

不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。

安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了,于是BitMover公司怒了,要收回Linux社区的免费使用权。

出现了这样的问题,Linus本可以向BitMover公司道个歉,保证以后严格管教弟兄们就可以继续使用BitKeeper。但是,实际情况是这样的:

Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!!

Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub。

Git简介

直接记录快照

Git对待或保存数据的方式:快照流。

Git 把数据看作是对小型文件系统的一系列快照。 在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。 为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。而Git也更像是一个小型的文件系统。

本地执行操作

在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。

这个时候可以回想一下,我们在离线或者没有VPN的时候,我们也可以进行git add … ,git commit…操作。而当你有网络的时候在上传。

三种状态

Git 有三种状态,你的文件可能处于其中之一: 已修改(modified)已暂存(staged)已提交(committed)

  • 已修改表示修改了文件,但还没保存到数据库中。
  • 已暂存表示对一个已修 改文件的当前版本做了标记,使之包含在下次提交的快照中。
  • 已提交表示数据已经安全地保存在本地数据库中。

这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。

  1. 工作区是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

    工作区就是你在电脑里能看到的目录,比如Data文件夹就是一个工作区

  2. 暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做 “索引(index)”,不过一般说法还是叫 “暂存区(staged)”。

  3. Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

我们知道当我们把文件往Git版本库也就是仓库目录里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

Git简单使用

你的工作目录下的文件无外乎处于两种状态:已跟踪未跟踪。已跟踪的文件实际上就是被Git所管控的文件,在已跟踪的文件里面,我们又可以对应到上面所说的Git三种状态。

浮图秀图片_git-scm.com_20211112092616.png

  • git init:初始化一个空的git仓库,这时候相应的文件夹下会自动生成.git的文件夹,就是工作区的一个隐藏目录.git,这个不算工作区,而是Git的版本库。

  • git status:检查当前的文件的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
(use "git add <file>..." to include in what will be committed)

READ

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: CONTRIBUTING.md

Untracked files:未跟踪文件,这样的文件常常是因为你新建了一个文件所造成的。

Changes to be committed:待提交的更改,表示文件正在暂存区。

Changes not staged for commit:未暂存清单,表示已跟踪文件的内容发生了变化,但还没有放到暂存区。

  • git add 跟踪文件:精确地将内容添加到下一次提交中。可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
  • git commit -m “提交所做的注解” :提交更新到仓库

  • git diff:显示暂未暂存的改动,并非所有改动

  • git rm 文件名:删除掉暂存区的文件,一般加上后缀–cached表示不删除原文件,只是从git暂存区删除,而-f(force)则是直接删除掉这个文件

  • git log:查看提交的历史,可以查看一个项目提交历史的各个人的提交

git fetch/pull

git pul git fetch

从图中我们可以看出,git fetch命令从服务器上抓取本地没有的数据,它只是更新了你的远程跟踪分支,并不会修改工作目录的内容,只会获得数据然后让你自己合并。git pull 在大多数的情况下他的含义是一个git fetch紧接着一个git merge命令。也就是与当前分支所跟踪的远程分支进行数据抓取然后合并。

远程仓库的使用

远程仓库是指托管在因特网或其他网络中的你的项目的版本库。 GitHub是个神奇的网站,从名字就可以看出,这个网站就是提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,在所有的工作开始之前可能需要你去获取并且设置一下你的SSH Key。

为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。

添加远程仓库

现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,达到一举多得的效果。

首先在Github上新建了一个仓库,找到这个仓库的地址。你会发现它不止有一个,Git 支持多种数据传输协议,默认的git://使用ssh,但也可以使用https等其他协议。

git remote add <shortname> <url>:添加一个新的远程git仓库,同时指定一个方便实用的简写

把本地库的内容推送到远程,用git push命令,实际上是把当前分支推送到远程。

在将本地的仓库和远程仓库进行连接以后,你就可以在提交文件到本地仓库之后在继续的push到远程仓库。

git的分支管理

分支的简单图解

在版本回退里,我们可以回溯到之前的任意一个版本里,而这些版本都处在一条时间线上,这条时间线就是一条分支

默认的分支是main(之前是master,后来由于这个词有歧视黑人的意思,就换了这里的图都是用的master),本身可以把它看成一个指针,head指针就是一个特殊的指针,这里就指向master指针

image-20201210015840577

我们在每次提交的时候,都会多出一个节点,指针也会随之移动,当我们创建一个新的分支,也就是创建一个新的指针,我们可以通过命令将head指针移动到新的指针上:

image-20201210020044381

我们还可以将两个分支合并:

image-20201210020232237

还可以在这里把dev分支给删掉

image-20201210020314110

分支的使用

  1. git branch :查看当前所有分支,及所在分支
  2. git branch 分支名 :创建新的分支
  3. git checkout 分支名:切换到别的分支
  4. (处于某个分支下)git merge 分支名:这就是将两个分支合并,此时两个分支就处在同一个节点下
  5. git branch -d :删除分支