# Outline
一、前言
二、概念
三、實驗
四、結語
A、延伸思考
# TL;DR
在〈Repository〉有提到 Repository 就是指 .git
本身,我們平時開發工作空間就是指 Working Directory,但更精確的說,是一個 Working Tree。Working Tree 是相依於一個 Repository 的,畢竟我們是從「倉儲」裡面搬檔案出來的,那是否有個可能事,我們能同時擁有多個 Working Tree?
或許我們都曾遇過類似的情境,我們在 A branch 進行開發,這時候突然想要參照另一個 branch、tag 或是 commit 版本的程式碼時該怎麼辦?或是我們因為一些緣故,不得不一直在兩個 branch 頻繁切換來開發?或許這時候我們都會很希望能同時開兩個 branch,也就是同時擁有多個 Working Tree。
幸運的是,Git 還真做得到。
在一開始或許我們可以先思考,要多一個獨立的 Working Tree,那在 Git Repository 的哪些檔案或是目錄也必須自己有獨立的?
基本上儲存在 Repository 的 Git Objects 和 Git Reference 都是可以共用的。那麼我們就是指向這些的資訊是要獨立的,也就是所謂的 HEAD
,透過獨立的 HEAD
讓我們的 Working Tree 可以有自己當前位置的參考,也就是可以和原本的 Working Directory 有不一樣的當前 Branch 或是 Commit 參考。
另位需要獨立的就是 Staging Area 了,也就是所謂的 index
,讓我們準備 commit 的暫存索引是和原本的 Working Directory 是分開的,才不會錯亂。
至於實際 Git 是獨立了哪些資訊,是怎麼讓 Working Tree 獨立的,就讓我們在下面的實驗解答吧!
# Command Synopsis: git-init
# Reference: https://git-scm.com/docs/git-init
git worktree add [OPTIONS] <path> [<commit-ish>]
Create <path> and checkout <commit-ish> into it. The new working directory is linked to the current repository, sharing everything except working directory specific files such as HEAD, index, etc.
OPTIONS
-f
--detach
--checkout
--lock
-b <new-branch>
-B
首先,先透過 git-worktree-add
指令去建立一個新的 Working Tree:
# Location: ~/how-git-works/lab
$ git worktree add ../lab-working-tree feature/ironman-git-tree
Preparing worktree (resetting branch 'feature/ironman-git-tree'; was at e2349d5)
HEAD is now at 81e9535 F
在上一層目錄透過 ls
指令檢視,的確多了一個 lab-working-tree
的目錄:
# Location: ~/how-git-works/
$ ls -l
drwxr-xr-x 4 ironman ironman 128 9 25 01:38 dot-git
drwxr-xr-x 4 ironman ironman 128 10 15 14:25 lab
drwxr-xr-x 4 ironman ironman 128 10 15 19:15 lab-working-tree
若是在該層目錄透過 ls
指令檢視,會驚奇的發現,.git
不是一個目錄,而是一個檔案:
# Location: ~/how-git-works/lab-working-tree
$ ls -l
-rw-r--r-- 1 ironman ironman 91 10 15 19:15 .git
-rw-r--r-- 1 ironman ironman 20 10 15 19:15 README.md
這時候我們嘗試用 cat
去顯示這個檔案的內容:
# Location: ~/how-git-works/lab-working-tree
$ cat .git
gitdir: /Users/ironman/how-git-works/.git/worktrees/lab-working-tree
這時候我們暸解到這裡的 .git
其實也是某種 reference,只不過和前面提到的 Git Reference 不一樣,他指向的不是一個 Git Commit Object,而是一個存放 Working Tree 資料的目錄,而這個目錄正位於我們原本 Repository 的 .git/worktrees
目錄底下。
再次透過透過 ls
指令檢視這個目錄:
# Location: ~/how-git-works/lab-working-tree
$ ls -l
total 40
-rw-r--r-- 1 ironman ironman 41 10 15 19:15 HEAD
-rw-r--r-- 1 ironman ironman 41 10 15 19:15 ORIG_HEAD
-rw-r--r-- 1 ironman ironman 6 10 15 19:15 commondir
-rw-r--r-- 1 ironman ironman 66 10 15 19:15 gitdir
-rw-r--r-- 1 ironman ironman 137 10 15 19:15 index
drwxr-xr-x 3 ironman ironman 96 10 15 19:15 logs
一一將這些檔案透過 cat
指令顯示出來:
$ cat HEAD
ref: refs/heads/feature/ironman-git-tree
$ cat ORIG_HEAD
81e953569c9c0740ddfadf16e1318451273335c1
$ cat commondir
../..
$ cat gitdir
/Users/ironman/how-git-works/lab-working-tree/.git
$ cat index
# 這邊會是亂數,因為 index 不是純文字檔案
HEAD
和 ORIG_HEAD
我們並不陌生,分別是當前 Working Tree 指向的位置以及進行危險動作前的位置。那麼 commondir
和 gitdir
是什麼呢?透過顯示的內容來看,可以得知 commondir
其實就是指向原本的 Repository .git
本身,讓我們知道去哪裡索取共用的資料。而 gitdir
就是指向 Working Tree 的 .git
路徑,讓我們 Git 可以隨時追蹤 Working Tree 的狀態,像是這個目錄是不是還在等等。
透過這一系列的實驗,我們大致就暸解 Git 是怎麼建立一個 Working Tree、有哪些資訊是和原本的 Working Directory 獨立的、又怎麼儲存了。