Git的基本概念

简介

Git 最初由 Linus Torvalds 编写,用于 Linux 内核开发的版本控制工具。Git 与常用的版本控制工具 CVS、Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持,使源代码的发布和交流极其方便。

GIT的基本概念

分布式而非集中式版本控制

cvcs.png

一个集中式的版本控制系统如上图所示。其特点就是有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。这种做法的好处是:每个人都可以在一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。这种做法最显著的缺点是:对于中央服务器的过度依赖,如果服务器宕机,那么大家将无法协同工作。甚至,如果中央服务器的磁盘发生故障,那还会有丢失数据的风险,最坏的情况可能会丢失整个项目所有的历史更改记录,而被客户端提取出来的某些快照数据除外。

dvcs.png

一个分布式的版本控制系统如上图所示。其特点是客户端并不是只提取最近版本的文件快照,而是把原始的代码仓库完整地镜像下来。这样就算任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库来恢复。每次提取操作实际上都是一次对代码仓库的完整备份。甚至,许多这类系统可以指定和若干不同的远端代码仓库进行交互。这样,你就可以在同一个项目中分别和不同的工作小组的人协作,你可以设定分层式的工作流,这在集中式的版本控制系统中是无法做到的。

Git 是一个分布式版本控制系统。 与此同时 Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。

如果你是一个具有使用 SVN 背景的人,你需要做一定的思想转换,来适应 Git 提供的一些概念和特征。

Git 与 SVN 区别点:

  • 1、Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 SVN,CVS 等,最核心的区别。
  • 2、Git 把内容按元数据方式存储,而 SVN 是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。
  • 3、Git 分支和 SVN 的分支不同:分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
  • 4、Git 没有一个全局的版本号,而 SVN 有:目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
  • 5、Git 的内容完整性要优于 SVN:Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

保存更新时的文件快照而非差异

Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。

如下图,其他系统在每个版本中记录着各个文件的具体差异:

deltarecord.png

而如下图,Git 保存每次更新时的文件快照,Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件做快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览所有文件的指纹信息并对文件做快照,然后保存一个指向这次快照的索引。对于没有发生变化的文件,Git 不会再次保存,只做一个链接指向上次保存的快照来提高性能。Git 更像是一个小型的文件系统。

snapshotrecordstyle.png

Git 采用这样的设计带来了这些好处:

  • 近乎所有的操作都是本地执行。
  • 时刻保持数据的完整性。在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。如果文件在传输时变得不完整,或磁盘损坏导致文件数据缺失,Git 能立刻察觉。
  • 大多数 Git 操作都只是添加数据。我们都知道数据删除了,那么想回退或重现就变得非常困难。在 Git 中大多数操作只是添加数据,回退和重现变得容易而有保障,所以提交快照后就完全不用担心丢失数据。

Git 管理下文件的状态

对于在 Git 管理下的文件,只可能有三种状态:

  • 已暂存(staged),表示对这个被修改的文件做了快照,并把快照放在下次提交时要保存的清单中了。
  • 已修改(modified),表示该文件当前被修改过了,但是还没提交保存,当前的状态也没有做快照暂存。
  • 已提交(commited),表示该文件已经被安全地保存在本地数据库中了。

所以基本的 Git 工作流程一般是这样的:

在工作目录中修改某些文件(modified) ===> 对修改后的文件进行快照,然后保存到暂存区域(staged) ===> 提交更新,将保存在暂存区域的文件快照永久转存到 Git 目录中(commited)

下面就来粗略说一下在 Git 管理下的文件在这三种状态下切换的情况以及对应的 Git 命令。先上个状态转换图:

gitfilestatusflow.jpg

当我们从某个地方 git clone 一份代码后,通常这时你的 work directory 是 clean 的,所有代码都是 commited 状态,在图中表示为 Commited-1 状态,我们就从这里开始解释接下来文件状态的切换:

  • Commited-1 ===> Modified,我们拿到代码开始干活,当我们修改了文件后,文件状态发生变化,由 Commited-1 变为 Modified。

  • Modified ===> Commited-1,发现文件改的不对,一处一处恢复太麻烦,用 git checkout 命令便可以把对文件的修改恢复到原 Commited-1 的状态。

  • Modified ===> Staged,文件修改的差不多了,想提交对这个文件的修改到本地 git 版本库保存起来,在这时你直接用 git commit 是提交不了的,因为 git commit 是对文件暂存区域的文件快照进行提交。所以,你需要先把文件快照一下放到暂存区域,用 git stage 命令搞定,另外,用 git add 也是一样的。

  • Staged ===> Modified,发现目前不想在下次 commit 时提交文件暂存区域的某个文件,还想再改改或再缓缓,这时你需要把它的快照撤回来,用 git reset 命令搞定。

  • Staged ===> s1,已经把某个文件快照后放到暂存区域了,但是又对它进行了修改。这时文件的状态就很麻烦了。这时你如果使用 git commit 提交,那么提交的是放在暂存区域的快照,而这之后的修改是不会提交的。

  • s1 ===> Staged,当然你可以用 git checkout 命令从这种比较麻烦的状态撤销修改,这时文件状态就又回到了 Staged。这里多说一句,看了上节介绍的 Git 总是保存文件更新时的快照,这里就可以这样理解:Git 管理下的文件在我们的折腾下,不停的在各种状态下流转,在不同的情况下,我们使用一些 Git 命令时会对文件拍快照,Git 就可以操作文件的快照了,这些快照对应的是一个文件的不同时刻的照片,文件是流动的,而快照则是某一时刻的永恒。

  • Staged ===> Commited-2,使用 git commit 命令就可以把暂存区域的文件快照提交到 Git 版本库永久存储了,这时文件状态转变为 Commited-2。记得养成写 commit message 的良好习惯。

  • Modified ===> Commited-2,看了上面的流程,有人会问了,我每次改了都要先 stage 才能提交还麻烦啊,有没有便捷点的方法?答案是:有。用 git commit -a 就可以把 Git 管理下的文件的修改都提交了。这里要注意的是,新增的文件是不在 Git 管理下的,需要 git add(stage) 一下才行。

  • 撤回提交。上面一切进行的还算顺利,各种状态跳来跳去都能搞定了,现在问题来了:我已经 commit 了后才发现,天啊,提交错了,我要撤回!别急,这也是可以搞定的,而且这里有两种选择,但是需要搞清楚它们的区别。另外,这两种撤回提交都可以指定撤回几次,感兴趣的可以再深入研究。
    用 reset 撤销提交。

    • reset 撤销就是真的删除了提交信息,回退到之前的状态了。不过有下面几种情况:
      • Commited-2 ===> Staged,从这次提交回到上次提交后的 Stage 状态用 git reset HEAD^ –soft 命令。
      • Commited-2 ===> Modified,从这次提交回到上次提交后的 Modified 状态用 git reset HEAD^ –mix 命令。上次提交后的修改都还在。–mix 是默认选项。
      • Commited-2 ===> Commited-1,从这次提交回到上次提交后的 Commited-1 状态用 git reset HEAD^ –hard 命令。注意,所有你上次提交后的修改都没了!
    • revert 反向提交。用 revert 也可以撤销此次提交,但是它跟 reset 是不一样的,revert 本质上也是一次提交,只不过是它是一次与上次提交相逆的提交。
      • Commited-2 ===> Commited-1,我们在 Commited-2 这个状态使用 git revert HEAD 命令,就可以直接回到 Commited-1 状态了。
      • Commited-1 ===> Commited-2,有意思的是,接着上次的 revert 我们在 Commited-1 这个状态再使用一次 git revert HEAD 命令,会发现我们又回到 Commited-2 状态了。这是合理的,逆提交的逆提交就变成正了。

到这里,Git 管理下的文件状态的转变和相关的命令就简要介绍完了,相信理解了这些,对于更好的使用 Git 会有很大的帮助。如果对各种更加细节的信息感兴趣可以去更深入的研究。

HEAD是什么

上面多处出现了一个叫 HEAD 的东西,这里简要介绍一下。HEAD 文件是一个指向你当前所在分支的引用标识符。这样的引用标识符——它看起来并不像一个普通的引用——其实并不包含 SHA-1 值,而是一个指向另外一个引用的指针。如果你看一下这个文件,通常你将会看到这样的内容:

cat .git/HEAD
ref: refs/heads/master

当你执行 git branch <分支名称> 这条命令的时候,Git 怎么知道最后一次提交的 SHA-1 值呢?答案就是 HEAD 文件。

如果你执行 git checkout test,Git 就会更新这个文件,看起来像这样:

cat .git/HEAD
ref: refs/heads/test

当你再执行 git commit 命令,它就创建了一个 commit 对象,把这个 commit 对象的父级设置为 HEAD 指向的引用的 SHA-1 值。

上面说的最后一句话不太好理解吧,接下来,看个例子。我们假设当前的代码分支情况如下图所示,当前,我们在 master 分支工作,HEAD 指向着当前的 master 分支。

head1.png

我们执行 git checkout testing 转换到 testing 分支。这时变化如下图,HEAD 指向了 testing 分支。

head2.png

这样的设计有什么好处呢?我们来修改文件并做一次提交试试:

vim test.txt
git commit -a -m 'made a change'

提交后发生的变化如下图所示,现在 testing 分支向前移动了一格,而 master 分支仍然指向原先 git checkout 时所在的 commit 对象。

head3.png

# git  shell 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×