版本控制
什么是“版本控制”?我为什么要关心它呢? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 在接下来所展示的例子中,我们对保存着软件源代码的文件作版本控制,但实际上,你可以对任何类型的文件进行版本控制。
如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本(这或许是你非常渴望拥有的功能),采用版本控制系统(VCS)是个明智的选择。 有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。 使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。
本地版本控制系统
许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
Figure 1. 本地版本控制.
其中最流行的一种叫做 RCS,现今许多计算机系统上都还看得到它的踪影。 甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。 它的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。
集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。 这类系统,诸如 CVS、Subversion(SVN) 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 多年以来,这已成为版本控制系统的标准做法。
Figure 2. 集中化的版本控制.
这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。 现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。
事分两面,有好有坏。 这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
.
分布式版本控制系统
于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
Figure 3. 分布式版本控制.
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的
Git 简史
同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。
Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统。 他们对新的系统制订了若干目标:
- 速度
- 简单的设计
- 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
- 完全分布式
- 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。 它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统。
Git 基础
那么,简单地说,Git 究竟是怎样的一个系统呢? 请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。 在开始学习 Git 的时候,请努力分清你对其它版本管理系统的已有认识,如 Subversion 和 Perforce 等;这么做能帮助你使用工具时避免发生混淆。 Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
直接记录快照,而非差异比较
Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 概念上来区分,其它大部分系统以文件变更列表的方式存储信息。 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。
Figure 4. 存储每个文件与初始版本的差异.
Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个快照流。
Figure 5. 存储项目随时间改变的快照.
这是 Git 与几乎所有其它版本控制系统的重要区别。 因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 稍后我们在Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。
近乎所有操作都是本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。 如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git 在这方面会让你感到速度之神赐给了 Git 超凡的能量。 因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。
举个例子,要浏览项目的历史,Git 不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。 你能立即看到项目历史。 如果你想查看当前版本与一个月前的版本之间引入的修改,Git 会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
这也意味着你离线或者没有 VPN 时,几乎可以进行任何操作。 如你在飞机或火车上想做些工作,你能愉快地提交,直到有网络连接时再上传。 如你回家后 VPN 客户端不正常,你仍能工作。 使用其它系统,做到如此是不可能或很费力的。 比如,用 Perforce,你没有连接服务器时几乎不能做什么事;用 Subversion 和 CVS,你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。 这看起来不是大问题,但是你可能会惊喜地发现它带来的巨大的不同。
Git 保证完整性
Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git 一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中增加数据。 很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦你提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
这使得我们使用 Git 成为一个安心愉悦的过程,因为我们深知可以尽情做各种尝试,而没有把事情弄糟的危险。 更深度探讨 Git 如何保存数据及恢复丢失数据的话题,请参考撤消操作。
三种状态
好,请注意。 如果你希望后面的学习更顺利,记住下面这些关于 Git 的概念。 Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。 已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。
Figure 6. 工作目录、暂存区域以及 Git 仓库.
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作`‘索引’’,不过一般说法还是叫暂存区域。
基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
如果 Git 目录中保存着的特定版本文件,就属于已提交状态。 如果作了修改并已放入暂存区域,就属于已暂存状态。 如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
理解暂存区
安装Git
1
2
3
4
5![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/34408c111d5bf92ee27c48ab420ce8ca.png)
6
在你开始使用 Git 前,需要将它安装在你的计算机上。 即便已经安装,最好将它升级到最新的版本。 你可以通过软件包或者其它安装程序来安装,或者下载源码编译安装。这里只介绍如何在Windows平台上的安装方式。
在 Windows 上安装 Git 也有几种安装方法。 官方版本可以在 Git 官方网站下载,网址为 http://git-scm.com/。另外,还有一个名为 Git for Windows的项目,和 Git 是分别独立的项目。你可以在Git for Window网站下载,网址为Git官网
另一个简单的方法是安装 GitHub for Windows。 该安装程序包含图形化和命令行版本的 Git。 它也能支持 Powershell,提供了稳定的凭证缓存和健全的 CRLF 设置。你可以在 GitHub for Windows 网站下载,网址为 Git官网
通过命令行使用Git
Git 有多种使用方式。 你可以使用原生的命令行模式,也可以使用 GUI 模式,这些 GUI 软件也能提供多种功能。 在接下来的课程中,我们将使用命令行模式。 这是因为只有在命令行模式下你才能执行 Git 的 所有命令,而大多数的 GUI 软件只实现了 Git 所有功能的一个子集以降低操作难度。 如果你学会了在命令行下如何操作,那么你在操作 GUI 软件时应该也不会遇到什么困难,但是,反之则不成立。 此外,由于每个人的想法与侧重点不同,不同的人常常会安装不同的 GUI 软件,但所有人一定会有命令行工具。
初次运行 Git 前的配置
既然已经在系统上安装了 Git,你会想要做几件事来定制你的 Git 环境。 每台计算机上只需要配置一次,程序升级时会保留配置信息。 你可以在任何时候再次通过运行命令来修改它们。
Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:
Git提供了3种级别的配置,分别是:系统级别、全局配置、仓库级别
- /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果使用带有 --system 选项的 git config 时,它会从此文件读写配置变量。
- ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 可以传递 --global 选项让 Git 读写此文件。
- 当前使用仓库的 Git 目录中的 config 文件(就是 .git/config):针对该仓库。
每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。
在 Windows 系统中,Git 会查找 $HOME 目录下(一般情况下是 C:\Users$USER)的 .gitconfig 文件。 Git 同样也会寻找 /etc/gitconfig 文件,但只限于 MSys 的根目录下,即安装 Git 时所选的目标位置。
用户信息配置
当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
再次强调,如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global 选项的命令来配置。
很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
检查配置信息
如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。
$ git config --list
user.name=John Doe
user.email=johndoe@example.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...
你可能会看到重复的变量名,因为 Git 会从不同的文件中读取同一个配置(例如:/etc/gitconfig 与 ~/.gitconfig)。 这种情况下,Git 会使用它找到的每一个变量的最后一个配置。
你可以通过输入 git config : 来检查 Git 的某一项配置
$ git config user.name
John Doe
获取帮助
若你使用 Git 时需要获取帮助,有以下种方法可以找到 Git 命令的使用手册:
$ git help <command>
$ git <command> --help
例如,要想获得 config 命令的手册,执行
$ git help config
这些命令很棒,因为你随时随地可以使用而无需联网。
Git常用命名
配置工具
为所有本地仓库配置用户信息。
git config –-list
查看所有的配置
git config --global user.name "[name]"
设置要附加到提交事务的名字
git config --global user.email "[email address]"
设置要附加到提交事务的邮箱地址
git config --global color.ui auto
启用命令行输出的帮助信息着色方案
git config --global core.editor "notepad"
配置默认的编辑器
创建仓库
初始化一个新存储库或从克隆一个现有的存储库
git init [repository-name]
以指定的名称创建一个新的本地仓库
git clone [url]
克隆一个远程仓库,仓库的整个版本历史都会被克隆下来
操作仓库
预览编辑并创建提交事务
git status
查看当前工作目录的状态,列出所有新创建的或者修改之后的文件。
git diff
显示修改之后,但未被保存到暂存区的文件与修改之前的差异。
git add [file]
为版本控制准备文件快照(将文件添加到暂存区或跟踪新建文件)
git diff --staged
显示暂存文件和最后一个版本之间的差异(显示文件快照与上一个版本之间的差异)。
git reset [file]
拆下(unstates)文件,但保留其内容(删除文件快照,文件的更改不会被提交)
git commit -m "[descriptive message]"
将文件快照永久地记录在版本历史记录中。
git commit -a -m/--message "[descriptive message]"
跳过暂存区,直接提交,但被提交的内容必须是已被git跟踪过的内容
分支
命名一系列提交,合并完成的效果
git branch
列出当前存储库中的所有本地分支
git branch [branch-name]
创建一个新分支
git checkout [branch-name]
切换到指定分支,并更新工作目录
git merge [branch]
将指定分支的历史记录合并到当前分支中
git branch -d [branch-name]
删除指定分支
删除或重命名
重新安置和删除版本化的文件
git rm [file]
从工作目录中删除文件并暂存删除操作。
git rm --cached [file]
从版本控制中删除文件,但是保留本地(工作目录中)文件。
git mv old-file-name new-file-name
更改文件名并准备提交
禁止跟踪
忽略临时文件和路径
*.log
build/
temp-*
在.gitignore文件中指定要不跟踪的文件和路径。
git ls-files --other --ignored --exclude-standard
列出此项目中所有被忽略的文件
查看版本历史
浏览和检查项目文件的发展
git log
列出当前分支的所有版本历史
git log --follow file-name
列出文件的版本历史记录,包括重命名
git diff [first-branch]...[second-branch]
显示两个分支之间的内容差异
git show [commit]
查看某次提交时所做的操作,显示提交的元数据(作者,邮箱,等)和对文件所做的改变。
重做提交
清除错误和工艺替换历史
git reset [commit]
在[commit]之后撤消所有提交,在本地保留更改
git reset --hard [commit]
丢弃所有的历史纪录和更改,回退到指定的提交
远程仓库
注册存储库书签并交换版本历史记录
git fetch [bookmark]
将数据拉取到你的本地仓库,但不会自动合并或修改你当前的工作。 你需要手动将其合并入你的工作。
git merge [bookmark]/[branch]
将书签的分支合并到当前本地分支
git push [remote-name] [branch-name]
将指定分支推送到远程服务器时。例如将 master 分支推送到 origin 服务器。
git pull
从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。