【工具箱】GIT的使用场景

无论学习什么技术,都需要了解该技术的本质。若是靠死记硬背该技术提供的方法或者语法,终归是知其然而不知其所以然,当发现错误时,你根本不知道是什么原因导致的。我在使用Git时,就处于这种知其然而不知其所以然的状态。现在,再来补补课。

Git有三个工作区域,分别为:工作目录(Working Directory)、暂存区(Stage或Index)以及资源库(Repository或Git Directory)。

参考Pro Git一书,它给出了Git的几个要点:

  • 直接快照,而非比较差异:Git与其他版本管理系统的主要差别在于,Git只关心文件数据的整体是否发生了变化,而其他多数版本管理系统则只关心文件内容的具体差异。Git并不保存文件前后变化的差异数据,更像是把变化的文件做一个快照,然后记录在一个微型的文件系统中。每次提交更新时,会比较这个快照。若文件没有变化,Git则只对上次保存的快照作一个链接。你可以理解Git就是一个小型的文件系统。
  • 近乎所有操作都可本地执行:无需多说,这本身就是分布式版本管理系统的特征。
  • 时刻保持数据完整性:保存到Git前,所有数据都要进行内容的校验和(checksum),并将该结果作为数据的唯一标识。Git使用了SHA-1算法计算数据的校验和,并将该结果作为索引,而非文件名。
  • 多数操作仅添加数据

Pro Git一书认为任何一个文件在Git内部可以被分为三种状态:已提交(Committed)、已修改(Modified)和已暂存(Staged)。然而,这并不足以说明一个文件在不同的工作区域所展现的状态。我认为两种状态足以表达Git中的文件,即:未跟踪(Untracked)和已跟踪(Tracked)。而对于已跟踪状态,我又将其分为:未修改的(Unmodified),Modified(已修改的),暂存的(Staged)和已提交的(committed)。下图基本表达了我的思路:

这个图表现了多种场景,满足了我们在使用Git时耳濡目染的操作情形。

场景1:暂存文件以及取消已暂存的文件

可以参考上图中上面部分黑色箭头标示。当我们通过git init在本地初始化了Git工作目录后,新增了一个README.txt文件时,此时该文件处于Untracked状态。接下来执行命令:

git add README.txt

add命令可以暂存此文件,此时,状态变更为Staged状态,被放到了Git暂存区中。若我们要提交此文件到Git资源库,就可以执行git commit命令,文件状态变为committed。例如:

git commit -m "first commit"

有时候,我们希望取消已暂存的文件。例如说,我在工作目录中增加了两个文件,然后暂存了它们。后来发现其中一个文件并不需要在Git中管理,希望能够取消暂存。由于此时的文件处于Staged状态,我们只需要删掉Stage中对此文件的跟踪即可。这时需要执行的命令是:

git rm --cached README.txt

注意:此时取消暂存的文件从来就不曾提交过,也即是说没有在Git Repository留下过它的身影。这时的取消暂存实则是删掉暂存的信息。与后面场景演示的取消暂存并不相同。

场景2:修改已提交文件以及取消已暂存的内容

一旦文件被提交,就会在Git Repository形成提交记录(以hash作为键)。倘若我们此时push提交到远程Git服务器,Git服务器应与本地库保持一致。

现在,让我们看看图中红色箭头展现的流程。我们修改了已提交的README.txt文件,于是文件状态就变更为Modified。这部分修改的内容并没有被放入暂存区,若要提交此次修改,就还需要再次执行git add命令,将这次新的修改放入到暂存区。这个流程包括后面的提交都与场景1相似。唯一不同的是“取消已暂存的内容”。

虽然同样是取消暂存,但它与场景1是完全不同的概念。场景1实则是要取消暂存区的文件,因此使用了git rm –cached,本质上讲其实是删除。而这里的取消,其实是希望取消暂存区中已经被添加的修改内容,文件本身仍然保留在暂存区中。故而执行的命令为:

git reset HEAD README.txt

HEAD是何意呢?在Git中,HEAD是一个特别的指针,指向你正在工作的本地分支。当前分支就是master。如下图所示:

而reset命令的意思是重新设置当前的HEAD指针到特定的状态。由于当前的README.txt还没有提交到master分支的Repository中。因此,这条命令实则就是将HEAD指向README.txt文件在当前master分支的Repository状态,从而保证了对README.txt文件而言,暂存区与Repository的一致——取消了README.txt文件在暂存区的内容。

场景3:修改文件以及撤销修改内容

再看图中的绿色箭头与蓝色箭头展现的流程。我们不是初始化git工作目录,而是通过git clone从远程Repository克隆了项目,此时会在当前目录建立git工作目录。此时的文件全部处于Unmodified状态。

现在,我们修改文件,例如hello.java。一旦被修改,文件状态就迁移到Modified状态。倘若需要暂存此次修改,甚至提交到Git Repository,则执行的流程与场景1相同(如蓝色箭头线所示)。

然而,我们可能希望放弃此次修改,即不将修改的内容放入暂存区。这时,应执行checkout命令:

git checkout -- hello.java

在执行checkout命令时要慎重。因为它要撤销的内容并没有被放入到暂存区或Repository。一旦撤销,就一去不复返了。

概念区分:fetch .vs. pull

fetch命令只是将远端数据拉到本地仓库,并不自动合并到当前工作分支。若要合并,还需手动合并。例如,执行git fetch origin,就会抓取自上次克隆以来别人上传到此远程仓库中的所有更新。pull命令则除了会抓取数据,还能将远端分支自动合并到本地仓库中当前分支。

场景4:撤销提交

在Git中若要撤销提交,可以使用reset或者revert命令。但二者有着显著的区别: revert命令可以撤销已经提交的快照,但它并不会将该提交从项目的提交历史中移除,而是会判断要撤销的这次提交引入了哪些变化,然后将此变化撤销(此次撤销事实上还是一种变化),再将这次撤销作为一个提交。因此,在执行revert命令后,如果通过git log查看提交历史,可以看到会新增一个revert提交。命令为:

git revert <commit>

这个commit可以是指定提交对应的hash code。我们也可以用HEAD指针:

git revert HEAD~n

如果是revert当前提交,则不需要HEAD后的~n。

reset命令就字面意义已经表达了该操作的含义为“重置”。由于Git的提交记录是由HEAD指针指向当前分支。重置就是搬动这个指针到指定的snapshot。如果说revert是一种安全的撤销方式,则reset就是一种危险的撤销方式。默认情况下,如果使用reset命令,会将当前的分支回退到指定commit,然后自指定commit到最新commit之间的内容会放在工作目录下,使得我们可以再提交。这个命令为:

git reset <commit>

与前相同,这个commit就是提交对应的hash code。同样,也可以使用HEAD指针。不过如果是撤销当前提交,与revert不同的是,需要指定为:HEAD~1。这是因为HEAD指针指向了当前提交。reset与revert的意义不一样。revert对应的commit为目标提交,意思为:“撤销目标提交”,因而git revert HEAD,代表的就是“将当前提交撤销”。而reset对应的commit表示将指针移向给定的Commit。如果执行git reset HEAD,代表的就是“将当前指针指向当前提交”,相当于没做任何操作。所以应该执行git reset HEAD~1。下图说明了reset与revert的区别:

如果确实要撤销操作,而前面的内容并不需要,在使用reset命令时,可以添加–hard参数:

git reset --hard <commit>

注意:针对远程的提交记录,应尽量避免使用git reset命令。倘若在本地进行了reset之后,又进行了另外的修改并提交。此时,本地的提交记录与远程的提交记录在reset的那个点产生了分叉。如下图所示:

此时,如果执行git push,会在本地合并后提交,并同步远程提交记录。则团队其他成员会因为这个变化的提交记录而困惑。由于一部分变更消失,甚至可能导致一些数据被破坏。因此,使用reset命令要格外当心,通常情况,应尽量针对本地提交(未push到远程)进行reset。优先考虑使用revert命令。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2014-08-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

图解CentOS系统启动流程

? 作者 | Linux运维之路 来源 | 51CTO博客 ? 云豆贴心提醒,本文阅读时间6分钟,文末有秘密! 当我们按下开机键后,系统背后的秘密我们...

4298
来自专栏xingoo, 一个梦想做发明家的程序员

[大数据之Sqoop] —— 什么是Sqoop?

介绍 ? sqoop是一款用于hadoop和关系型数据库之间数据导入导出的工具。你可以通过sqoop把数据从数据库(比如mysql,oracle)导入到hdfs...

23110
来自专栏https://www.cnblogs.com/L

【Impala篇】---Hue从初始到安装应用

Cloudera公司推出,提供对HDFS、Hbase数据的高性能、低延迟的交互式SQL查询功能。基于Hive使用内存计算,兼顾数据仓库、具有实时、批处理、多并发...

1052
来自专栏玄魂工作室

[小技巧]使用weeman来做一个钓鱼网页

工具项目地址:https://github.com/Hypsurus/weeman/ 克隆地址:https://github.com/Hypsurus/weem...

3134
来自专栏吴小龙同學

手把手教你建github技术博客by hexo

适合人群 喜欢写 Blog 的人 有一定的编程基础 爱折腾的人 熟练使用版本控制 Git 了解使用 GitHub 熟悉基本的 MarkDown 语法 环境准备 ...

3667
来自专栏磨磨谈

Ceph用户邮件列表Vol45-Issue4

It is expected that the cluster log will be flooded with messages like: 2016-07-...

833
来自专栏从零开始学自动化测试

Selenium2+python自动化1-最新环境搭建

前言 目前selenium版本已经升级到3.0了,网上的大部分教程是基于2.0写的,所以在学习前先要弄清楚版本号,这点非常重要。本系列依然以selenium2为...

2765
来自专栏有刻

Docker 小记 — Compose & Swarm

36011
来自专栏Java工程师日常干货

工程化专题之Git前言Github && Git 中的一些重要概念 Git在实际中的使用方式Git常用命令总结

一个专业的程序员,怎么能不掌握Git呢?版本控制领域,已经发展了一段时间,从古老的CVS,到SVN集中式管理,再到现在的Git。由于Git的分布式、everyt...

772
来自专栏程序手艺人

Android中Repo 常用命令参考

3445

扫码关注云+社区