日常工作应了解的 Git 基本原理
随着 Git 的发展,它已经成为程序员必须了解的分布式版本控制器了,工作中不一定需要你搭建 Git 服务器,但肯定会让你使用它来协同开发。
这篇文章没有讲如何使用 Git,只描写了一个不太会写代码的开发人员应该掌握的基本原理,没有什么深度可言,如果想要更加深入的了解搭建 Git 服务器等知识,可以直接阅读Pro Git book[1],本文也只是其中一部分知识的小结。
Git 历史
版本控制器是一种记录一个或若干文件内容变化,以便将来查阅特定版本的修订情况,通过它记录的数据我们就可以知道一个文件是如何一步步发展到今天这个样子的,也就是这个文件的每个版本你都能看到。
之所以会诞生 Git,是因为开发 BitKeeper(一个版本控制器) 的商业公司与 Linux 内核社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力,这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出了自己的版本控制系统。
因为 Linus 自己就是内核专家与文件专家,所以 Git 也就自然而然也就具备了非凡的存储能力与性能。
Git 基础
Git 和大多数版本控制器有一个重要的区别,就是它直接记录快照,而非差异比较,即把代码仓库完整地镜像下来,换个土一点的说法就是,只要你的文件有改动,那么 Git 就会把这个文件复制一份,这看起来很像我们学生时代的备份方式,但 Git 不会复制没有修改的文件,只是保留一个链接指向之前存储的文件。
正因为 Git 存储的是一个快照流,所以 Git 实际上很占用存储空间,Git 仓库很容易就会变得很大很大,而 Git 快的原因也和这种存储方式有一丝关系,因为本地已经具备基本所有的数据,它近乎所有的操作都是本地执行的,所以不管有没有网络你都能使用它,你本地就是一个完整的仓库。
请记住 Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示数据已经安全的保存在本地数据库中。已修改表示修改了文件,但还没保存到数据库中。已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
由此我们引入 Git 项目的三个区域概念:Git 仓库、工作目录和暂存区域。
基本的 Git 工作流程如下:
•在工作目录中修改文件;•暂存文件,将文件的快照放入暂存区域;•提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
你工作目录下的每个文件不外乎两种状态:已跟踪或未跟踪。已跟踪是指那些纳入了版本控制的文件,在上一次快照中有它们的记录,上面提到的 Git 三种状态都属于已跟踪状态;当然,除了已跟踪文件就是未跟踪文件,这部分文件没有存在于上次的快照记录中,所以如果你把它们删了的话,那它们可能就是真的被删了。
你可以使用git status命令查看文件的状态,更多关于 Git 文件的操作可以查看记录每次更新到仓库[2]。
远程仓库
你可能经常使用git push origin master命令,其中的origin就是远程仓库服务器的名字,origin 是 Git 给你克隆的仓库服务器取的默认名字,它和其它名字并没有什么不同,只不过大家都懒得去改它,所以它就变得貌似特殊了。
在拉取远程代码时你可能会使用git pull,它本质上是两条命令的组合,即git fetch和git merge,在 Git 中有很多这样的命令,它只是为了方便我们而简单的把几个命令组合起来了而已,比如git mv、git clone等等。
需要注意的是,由于团队协作的原因,我们大部分情况下是落后于远程仓库进度的,所以经常会在拉取远程代码时遇到冲突,Git 会像下面这样来标记冲突内容。
<<<<<<< HEAD:index.html<div id="footer">contact : email.support@github.com</div>=======<div id="footer"> please contact us at support@github.com</div>>>>>>>> testing:index.html
分支管理
Git 的分支模型是它的必杀技特性,它处理分支的方式是难以置信的轻量,创建分支几乎是在一瞬间完成,而且在不同分支间的切换也非常的便捷,要理解 Git 的分支,我们必须再次回顾 Git 是如何保存数据的,它存的是一个快照流。
下图是我们的一个工作(快照)流,可以看到所谓的分支实际上就是一个可以移动的指针而已,master、v1.0都仅仅是一个指针,而创建分支、切换分支等操作也都只是对指针的操作,因此就不奇怪为什么 Git 这么快了。
那么 Git 又是如何知道当前在哪一个分支上呢?它仅仅是用了一个名为HEAD的特殊指针,你可以将HEAD想象为当前分支的别名,HEAD指向哪个分支,就表示当前处于哪个分支。
Git 也鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。我们从一个分支切换到另一个分支,本质上只是回到某个历史快照的状态下而已,在哪个分支下工作就会移动这个分支的指针,而其它分支的指针则不会移动,因此给我们的感觉就好像一棵树逐渐长出枝叶一样。
需要注意的是,master也不是什么特殊的分支,它和其它分支完全没有区别,之所以几乎每一个仓库都有master分支,是因为git init命令默认创建了它,并且大多数人都懒得去改动。
远程分支以(remote_name)/(branch_name)的形式命名,你从从远程仓库克隆一个仓库到本地,那么本地仓库就会有一个默认的远程分支指针,比如你仅仅使用git clone克隆了一个仓库,那么你的远程默认分支名字就是origin/master。
远程仓库是下图上半部分那个样子,克隆到你本地后它就变成了下半部分那个样子,origin/master指针不会随着你在本地的提交而移动,如果你需要同步你的工作,运行git fetch origin命令,它就会把origin/master指针移动到新的、更新后的位置。
参考内容:
[1] Pro Git book: https://git-scm.com/book/zh/v2
个人公众号:刘小绪同学
#搞技术你要知道#