图文详解 Git 的使用场景

Git有三个工做区域,分别为:工做目录(Working Directory)、暂存区(Stage或Index)以及资源库(Repository或Git Directory)。下图是文件在这三个工做区域之间的关系:java

参考Pro Git一书,它给出了Git的几个要点: * 直接快照,而非比较差别:Git与其余版本管理系统的主要差异在于,Git只关心文件数据的总体是否发生了变化,而其余多数版本管理系统则只关心文件内容的具体差别。Git并不保存文件先后变化的差别数据,更像是把变化的文件作一个快照,而后记录在一个微型的文件系统中。每次提交更新时,会比较这个快照。若文件没有变化,Git则只对上次保存的快照做一个连接。你能够理解Git就是一个小型的文件系统。 * 近乎全部操做均可本地执行:无需多说,这自己就是分布式版本管理系统的特征。 * 时刻保持数据完整性:保存到Git前,全部数据都要进行内容的校验和(checksum),并将该结果做为数据的惟一标识。Git使用了SHA-1算法计算数据的校验和,并将该结果做为索引,而非文件名。git

* 多数操做仅添加数据算法

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

这个图表现了多种场景,知足了咱们在使用Git时耳濡目染的操做情形。服务器

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

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

 
  1. git add README.txt

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

 
  1. git commit -m "first commit"

有时候,咱们但愿取消已暂存的文件。例如说,我在工做目录中增长了两个文件,而后暂存了它们。后来发现其中一个文件并不须要在Git中管理,但愿可以取消暂存。因为此时的文件处于Staged状态,咱们只须要删掉Stage中对此文件的跟踪便可。这时须要执行的命令是:指针

 
  1. git rm --cached README.txt

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

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

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

如今,让咱们看看图中红色箭头展示的流程。咱们修改了已提交的README.txt文件,因而文件状态就变动为Modified。这部分修改的内容并无被放入暂存区,若要提交这次修改,就还须要再次执行git add命令,将此次新的修改放入到暂存区。这个流程包括后面的提交都与场景1类似。惟一不一样的是“取消已暂存的内容”。

虽然一样是取消暂存,但它与场景1是彻底不一样的概念。场景1实则是要取消暂存区的文件,所以使用了git rm –cached,本质上讲实际上是删除。而这里的取消,实际上是但愿取消暂存区中已经被添加的修改内容,文件自己仍然保留在暂存区中。故而执行的命令为:

 
  1. 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命令:

 
  1. git checkout -- hello.java

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

概念区分:fetch vs. pull

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

pull命令则除了会抓取数据,还能将远端分支自动合并到本地仓库中当前分支。

场景4:撤销提交

在Git中若要撤销提交,可使用reset或者revert命令。但两者有着显著的区别:

revert命令能够撤销已经提交的快照,但它并不会将该提交从项目的提交历史中移除,而是会判断要撤销的此次提交引入了哪些变化,而后将此变化撤销(这次撤销事实上仍是一种变化),再将此次撤销做为一个提交。所以,在执行revert命令后,若是经过git log查看提交历史,能够看到会新增一个revert提交。命令为:

 
  1. git revert <commit>

这个commit能够是指定提交对应的hash code。咱们也能够用HEAD指针:

 
  1. git revert HEAD~n

若是是revert当前提交,则不须要HEAD后的~n。

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

 
  1. 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命令时,能够添加–hard参数:

 
  1. git reset --hard <commit>

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

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

相关文章
相关标签/搜索