Learn Git in 30 days——第 07 天:解析 Git 資料結構 - 索引結構

寫的非常好的一個Git系列文章,強烈推薦

原文鏈接:https://github.com/doggy8088/Learn-Git-in-30-days/tree/master/zh-cn

我們知道在 Git 裏兩個重要的資料結構,分別是「物件」與「索引」,這篇文章主要用來解說「索引」的細節。使用 Git 版本控制的過程中,或許你可以很輕易的瞭解 git 指令的用法,不過那很容易流於死記,無法靈活運用,連 Linus Torvalds 都在郵件清單(Mailing List)中提到:「在使用者瞭解索引的意義之前,是無法完整了解 Git 的能力的」,因此,瞭解「索引」的用途十分重要。

關於索引

簡單來說,「索引」的目的主要用來記錄「有哪些文件即將要被提交到下一個 commit 版本中」。

換句話說,「如果你想要提交一個版本到 Git 倉庫,那麼你一定要先更新索引狀態,變更纔會被提交出去。」

這裏的「索引」其實在國外很多文章裏曾經出現過很多別名,但其意思都是相同的,各位以後看到相關單字千萬不要被混淆了。

  • Index (索引)
  • Cache (快照)
  • Directory cache (目錄快照)
  • Current directory cache (當前目錄快照)
  • Staging area (等待被 commit 的地方)
  • Staged files (等待被 commit 的文件)

舉個例子來說,指令 git diff --cached 就與 git diff --staged 是完全同義的。

操作索引的指令

由於「索引」對 Git 來說十分重要,在大多數的指令中都會有跟 Git 索引相關的參數可用,不過我們大致列出幾個直接與「索引」相關的指令來解說。

在解說指令之前,各位可以先看看以下示意圖,這說明了通過指令改變狀態的生命週期,事實上,這些改變的過程,都是在更新「索引檔」的過程:

image

首先,先介紹四種文件狀態:

  • untracked (未追蹤的,代表尚未被加入 Git 倉庫的文件狀態)
  • unmodified (未修改的,代表文件第一次被加入,或是文件內容與 HEAD 內容一致的狀態)
  • modified (已修改的,代表文件已經被編輯過,或是文件內容與 HEAD 內容不一致的狀態)
  • staged (等待被 commit 的,代表下次執行 git commit 會將這些文件全部送入版本庫)

git status

取得 工作目錄 (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
 

這裏你會看到有三種不同的分組,分別是:

  • Changes to be committed (準備提交的變更)
    • 這區有個 new file: c.txt 文件,代表 c.txt 是一個新文件,而且已經被標示可提交。
    • 這代表着幾件事:
      1. 目前最新版 並沒有 c.txt 這個文件
      2. 索引檔 已經加入了這個 c.txt 文件
      3. 所以該文件會在執行 git commit 之後被存入下一個版本
  • Changes not staged for commit (尚未準備提交的變更)
    • 這區有個 modified: a.txt 文件,代表 a.txt 已經被變更,但尚未標示可提交。 (not staged)
    • 這代表着幾件事:
      1. 目前最新版 也有 a.txt 這個文件
      2. 索引檔 尚未加入 a.txt 這個文件
      3. 所以該文件就算執行了 git commit 也不會在下一版中出現
  • Untracked files (未追蹤的變更)
    • 這區有個 b.txt 文件,代表 b.txt 尚未被追蹤。(untracked)
    • 這代表着幾件事:
      1. 目前最新版 沒有 b.txt 這個文件
      2. 索引檔 也沒有 b.txt 這個文件
      3. 所以該文件就算執行了 git commit 也不會在下一版中出現

所以你可以看到,執行 git status 就是爲了查出 目前最新版 與 索引檔 之間的差異,最終只有 目前最新版 與 索引檔 之間有差異的變更,纔會真正儲存到下一個 commit 物件裏。

git add

git add 指令,是爲了將目前「工作目錄」的變更寫入到「索引檔」裏。

使用 git add -u 則可以僅將「更新」或「刪除」的文件變更寫入到「索引檔」中。

git rm

我們以 git rm 爲例,當你直接在文件系統中刪除一個文件,這只是從「工作目錄」中刪除而已,並沒有更新到索引檔,你可以利用 git status 看到這層改變,不過若要真正把「刪除」的狀態寫進索引檔的話,則要靠 git rm filename 更新索引檔。

在執行 git rm filename 的時候,除了更新索引檔之外,連工作目錄下的文件也會一併被刪除。若你只想刪除索引檔中的該檔,又要保留工作目錄下的實體文件,那麼你可以在指令列加上 --cached 參數,就能做到,例如:

git rm --cached a.txt 
 

git mv

使用 git mv oldname newname 可以將文件更名,執行此命令會同時更新索引與變更工作目錄下的實體文件。

git commit

這個指令,則是把「索引檔」與「目前最新版」中的資料比對出差異,然後把差異部分提交變更成一個 commit 物件。

git ls-files

在索引檔之中,預設就包含了 目前最新版 的所有文件,外加你在工作目錄中新增文件且通過 git add 更新索引檔後的那些文件。通過 git ls-files 命令,可以列出所有目前已經儲存在「索引檔」中的那些文件路徑。

從如下圖範例,你應該可以看出這幾個指令之間的關係:

image

今日小結

Git 裏的「索引」是 Git 版控中最重要的觀念,有了這層觀念,也自然能得知,爲什麼每次提交變更都要打一些指令把變更給加進去。當然,也有許多好用的 GUI 工具可以幫你少打許多指令,不過在我們正式開始使用 Git 的 GUI 工具之前,我們還是多靠指令把觀念給建立再說吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章