我们知道在 Git 裡两个重要的资料结构,分别是「物件」与「索引」,这篇文章主要用来解说「索引」的细节。使用 Git 版本控管的过程中,或许你可以很轻易的了解 git 指令的用法,不过那很容易流于死记,无法灵活运用,连 Linus Torvalds 都在邮件清单(Mailing List)中提到:「在使用者了解索引的意义之前,是无法完整了解 Git 的能力的」,因此,了解「索引」的用途十分重要。
简单来说,「索引」的目的主要用来纪录「有哪些档案即将要被提交到下一个 commit 版本中」。
换句话说,「如果你想要提交一个版本到 Git 储存库,那麽你一定要先更新索引状态,变更才会被提交出去。」
这裡的「索引」其实在国外很多文章裡曾经出现过很多别名,但其意思都是相同的,各位以后看到相关单字千万不要被混淆了。
举个例子来说,指令 git diff --cached
就与 git diff --staged
是完全同义的。
由于「索引」对 Git 来说十分重要,在大多数的指令中都会有跟 Git 索引相关的参数可用,不过我们大致列出几个直接与「索引」相关的指令来解说。
在解说指令之前,各位可以先看看以下示意图,这说明了透过指令改变状态的生命週期,事实上,这些改变的过程,都是在更新「索引档」的过程:
首先,先介绍四种档案状态:
取得 工作目录 (working tree) 下的状态。
由于先前已经讲过储存库、工作目录、物件与索引之间的关系,我们用一句话说明这关系:
Git 储存库的运作,是将工作目录裡的变化,透过更新索引的方式,将资料写入成 Git 物件。
这裡的 git status
指令,目的是显示出 目前最新版 与 索引档 之间的差异,这当中的差异包含了一些微妙的关系,我们用一个例子来解释这层关系。
以下是执行 git status 的结果:
G:\git-demo>git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: c.txt
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: a.txt
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# b.txt
这裡你会看到有三种不同的分组,分别是:
new file: c.txt
档案,代表 c.txt 是一个新档案,而且已经被标示可提交。modified: a.txt
档案,代表 a.txt 已经被变更,但尚未标示可提交。 (not staged)b.txt
档案,代表 b.txt 尚未被追踪。(untracked)所以你可以看到,执行 git status 就是为了查出 目前最新版 与 索引档 之间的差异,最终只有 目前最新版 与 索引档 之间有差异的变更,才会真正储存到下一个 commit 物件裡。
git add
指令,是为了将目前「工作目录」的变更写入到「索引档」裡。
使用 git add -u
则可以仅将「更新」或「删除」的档案变更写入到「索引档」中。
我们以 git rm
为例,当你直接在档案系统中删除一个档案,这只是从「工作目录」中删除而已,并没有更新到索引档,你可以利用 git status 看到这层改变,不过若要真正把「删除」的状态写进索引档的话,则要靠 git rm filename
更新索引档。
在执行 git rm filename
的时候,除了更新索引档之外,连工作目录下的档案也会一併被删除。若你只想删除索引档中的该档,又要保留工作目录下的实体档案,那麽你可以在指令列加上 --cached
参数,就能做到,例如:
git rm --cached a.txt
使用 git mv oldname newname
可以将档案更名,执行此命令会同时更新索引与变更工作目录下的实体档案。
这个指令,则是把「索引档」与「目前最新版」中的资料比对出差异,然后把差异部分提交变更成一个 commit 物件。
在索引档之中,预设就包含了 目前最新版 的所有档案,外加你在工作目录中新增档案且透过 git add
更新索引档后的那些档案。透过 git ls-files
命令,可以列出所有目前已经储存在「索引档」中的那些档案路径。
从如下图范例,你应该可以看出这几个指令之间的关系:
Git 裡的「索引」是 Git 版控中最重要的观念,有了这层观念,也自然能得知,为什麽每次提交变更都要打一些指令把变更给加进去。当然,也有许多好用的 GUI 工具可以帮你少打许多指令,不过在我们正式开始使用 Git 的 GUI 工具之前,我们还是多靠指令把观念给建立再说吧!