Git的使用场景

标签: git | 发表时间:2013-12-24 21:38 | 作者:
出处:http://agiledon.github.com/

无论学习什么技术,都需要了解该技术的本质。若是靠死记硬背该技术提供的方法或者语法,终归是知其然而不知其所以然,当发现错误时,你根本不知道是什么原因导致的。我在使用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命令时,可以添加–hard参数:

      git reset --hard <commit>

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

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

相关 [git] 推荐:

Git基础

- Wolf - 潘魏增
上个月末在公司内部作了一次《Git基础》的主题分享. 这里把分享内容公布出来,希望对一些朋友有用. 如果之前没有接触过Git,wikipedia上面已经有非常好的介绍. pdf格式:http://panweizeng.com/download/git-basics-meituan.pdf. keynote格式:http://panweizeng.com/download/git-basics-meituan.key.

Git-rebase 小筆記

- lepture - YORKXIN×YORKXIN
最近剛好有個機會整理很亂的 Git commit tree,終於搞懂了 rebase 的用法,筆記一下. 大家都知道 Git 有個特色就是 branch 開很大開不用錢,但很多 branches 各自開發,總要在適當時機 merge 進去 master. 看過很多 git 操作指南都告訴我們,可以妥善利用 rebase 來整理看似很亂或是中途可能不小心手滑 commit 錯的 commits ,甚至可以讓 merge 產生的線看起來比較簡單,不會有跨好幾十個 commits 的線.

Git 简明教程

- satoru - python.cn(jobs, news)
Git 是一款强大的分布式版本控制系统.在他的官网可以找到已经有很多著名的项目正在使用. Like most other modern version control systems, Git gives each developer a local copy of the entire development history, and changes are copied from one such repository to another.

git架构图解

- - CSDN博客研发管理推荐文章
  最近又遇到Git了,发现网络上Git的资料确实不咋滴,难懂不全面. 至于Git是什么我就不多说了,相比svn上手确实更难. 与svn集中版本库相比较,Git被称作分布式版本库,在分布式的版本库中任何一个库都可以作为中心库看待. 如果说svn是颗树,那么Git就像一张网. Svn在每个目录都有一个.svn文件夹存放信息,而git只在根目录才有,这就决定了svn可以单独拉取某个子目录或者某个文件,而git需要全部拉取.

科普:Git Commit Guidelines

- - IT瘾-dev
降低Review成本,可以明确知道本次提交的改变和影响. 规范整个Team的提交习惯,对技术素养的养成有益. 可以通过统一工具,抽取规范的message自动形成change log. 目前Github的Angular项目,就是完全采用规范的Git Message来进行日常的提交管理和发布管理的,下面是这个项目的Commit记录,和自动根据commit生成的change log.

一些 Git 設定偏好

- dylan - ihower { blogging }
讓 command line 指令列顯示目前處在哪一個 Git Brnach,最早是在 RGBA 看到這一招,非常方便. 請修改家目錄的 ~/.bashrc 或 ~/.bash_profile 檔案:. 記得打開 Git 的 color 設定,這樣 Git 指令的輸出結果才會加上顏色,像是 git status 等:.

Git和Mercurial(Hg)的分析

- gOODiDEA - 译言-电脑/网络/数码科技
来源Analysis of Git and Mercurial. 原文地址:http://code.google.com/p/support/wiki/DVCSAnalysis. (译者注:Mercurial以下简称Hg). 注:这篇分析完成于2008年夏季,当时我们正第一次为Google Code支持DVCS而作的研究工作.

GoogleCode 的 git 使用小记

- Fstone - Gracecode.com
早先就知道 GoogleCode 支持 git,不过一直没时间体验. 近期实在受不了频繁的 svn commit 加上公司的联通网络访问 GoogleCode 实在是慢得让人无法忍受,于是咬咬牙想把 GoogleCode 中那陈年的代码迁移到 git 控制中. 总得来讲,设置 GoogleCode 项目中新的版本控制方案并不复杂,只需要在管理中点击需要的版本控制系统就行.

理解Git工作流

- joyoner - FeedzShare
来自: 黑客志 - FeedzShare  . 发布时间:2011年08月04日,  已有 2 人推荐. 如果你不了解Git背后的设计初衷,那么你正处在危险境地,当然有很多参数可以强迫Git按照你的意愿行事,但这并不是Git被设计的工作方式,这就好比你可以把改锥当锤子使用,并且它也可以完成工作,但这对改锥没什么好处.

git和github简介(上)

- linyehui - 没做完,没准备好
在此贴上本人在Web标准化交流会6月25日北京站的主题分享. 在线PPT:http://jinjiang.github.com/slides/learning-git/. PPT源码:https://github.com/Jinjiang/slides/tree/gh-pages/learning-git.