Git 基礎


Posted by Torai on 2021-04-18

把 Git book 的內容簡短整理。

集中式 VCS

如:CVS、Subversion 和 Perforce。都有一個伺服器來管理所有版本的檔案,而許多用戶端會連到這台伺服器取出檔案來使用。

central VCS

圖一、集中化的版本控制系統。

缺點:中央伺服器如果發生故障的時候。 如果當機一小時,那麼這個小時之中,沒有人可以提交更新,也就無法協同合作。如果中心版本庫的硬碟發生損壞,又沒有做適當的備份,那麼你就絕對會遺失所有資料——包括專案的全部變更歷史,只會剩下用戶端各自機器上保留的單獨快照。

分散式 VCS

用戶端並不只取出最新的檔案快照;還把整個倉儲做個鏡像。 假設有任何一個協同合作的伺服器故障,事後都可以用任何一個用戶端的鏡像來還原。

DVCS

圖二、分散式版本控制系統。


記錄檔案快照,而不是差異

Git 把它的資料視為一連串的快照(Snapshot)。 每當你提交(commit)時,Git 會紀錄下你所有目前檔案的樣子,並且參照到這次快照中。 只要檔案沒有變更,Git 不會再度儲存該檔案,而是直接將上一次相同的檔案參照到這次快照中。

將檔案存成許多次的快照

圖三、將檔案存成許多次的快照。

Git 物件在儲存前都會被計算校驗碼(checksum)並以校驗碼參照物件,用來計算校驗碼的機制稱為 SHA-1 雜湊演算法。資料庫內,每個檔案都是用其內容的校驗碼來儲存。

幾乎所有的動作都只是增加資料到 Git 的資料庫。即使是 git rebase HEAD^ 造成的變更歷史還是會被 Git 記下來。

檔案的狀態

檔案的狀態
圖四、檔案的狀態

未追蹤(Untracked):Git 不會記錄此檔案的變動。

已追蹤(tracked):

未修改(Unmodified、committed):從上次提交後還沒編輯過,檔案己安全地存在你的本地端資料庫。

已修改(Modified):檔案已被修改但尚未提交到本地端資料庫。

已預存(Staged):經過 git add 的檔案,會被放入預存區(Staging area),準備在下次提交存到 Git repository。

工作目錄,預存區及 Git 資料夾

圖五、工作目錄,預存區及 Git 資料夾。

  1. Git 資料夾(Repository):是 Git 用來儲存你專案的後設資料及物件資料庫的地方。 當你克隆一個其他電腦的儲存庫時,這個資料夾也會被同時複製。
  2. 工作目錄(Working Directory):專案被檢出(checkout)的某一個版本。 這些檔案從 Git 目錄內被壓縮過的資料庫中拉出來並放在硬碟供你使用或修改。
  3. 預存區(Staging Area/ Index)是一個單一檔案,一般來說放在 Git 目錄下,儲存關於下次提交的資訊。

Git 工作流程大致如下:

  1. 你在你工作目錄修改檔案。
  2. 預存檔案,將檔案的快照新增到預存區。
  3. 提交,這會讓存在預存區的檔案快照永久地儲存在 Git 目錄中。

可隨時用 git status 查詢工作目錄下檔案的狀態。

git status -s 精簡輸出,左邊欄位用來指示「預存區」狀態,右邊欄位則是「工作目錄」狀態。

git diff 比對「工作目錄」和「預存區」之間的版本, 然後顯示尚未被存入預存區的修改內容。

git diff --staged 檢視已經預存而接下來將會被提交的內容,這個命令會比對「預存區」和「最後一次提交」。


取得一個 Git Repository

有兩種主要方法來取得一個 Git 倉儲。 第一種是將現有的專案或者資料夾匯入 Git; 第二種是從其它伺服器克隆(clone)一份現有的 Git 倉儲。

在現有資料夾中初始化 Repository

  1. 進入該專案的資料夾並執行 git init

    這個命令將會建立一個名為 .git 的子資料夾,其中包含 Git 進行版本控制所需要的所有檔案。

  2. git add .

    追蹤(track)所有檔案,放進預存區(staging area)建立一份快照(snapshot)。

  3. git commit -m "initial project version"

    提交(commit)預存區的檔案,把快照記錄在 Repository。

clone 現有的 Repository

進入該專案的資料夾執行git clone <url>,將遠端的 repository,複製一份到本地端。


忽略不需要的檔案

若有不想讓 Git 自動加入,也不希望它們被顯示為未追蹤的檔案,可以新建一個名為 .gitignore 的檔案,在該檔中列舉符合這些檔名的模式(pattern)。

pattern 有以下規則:

  • 空行及 # 開頭的行都會被忽略。
  • 使用 standard glob patterns,遞迴套用到整個 work tree 下。
  • 以斜線 / 開頭以避免遞迴(只忽略特定路徑的檔案)。
  • 以斜線 / 結尾代表是目錄。
  • 驚嘆號 ! 代表模式規則反向。

Glob 模式就像是 Shell 所使用的簡化版正規運算式(regular expressions)。

  • 一個星號(*)匹配零個或多個字元
  • [abc] 匹配中括弧內的其中一個字元(此例為 a、b、c)
  • 問號(?)匹配單一個字元
  • 中括孤內的字以連字號連接(如:[0-9])用來匹配任何在該範圍內的字元(此例為 0 到 9);
  • 也可以使用二個星號用來匹配巢狀目錄;a/**/z 將會匹配到 a/z、a/b/z、a/b/c/z 等等。
# .gitignore 範例
# 不要追蹤檔名為 .a 結尾的檔案
*.a

# 但是要追蹤 lib.a,即使上面已指定忽略所有的 .a 檔案
!lib.a

# 只忽略根目錄下的 TODO 檔案,不包含子目錄下的 TODO
/TODO

# 忽略 build/ 目錄下所有檔案
build/

# 忽略 doc/notes.txt,但不包含 doc/server/arch.txt
doc/*.txt

# 忽略所有在 doc/ 目錄底下的 .pdf 檔案
doc/**/*.pdf

追蹤新的檔案

git add 一個多重用途的指令:

  1. 用來「開始追蹤」檔案、
  2. 「預存」檔案
  3. 做一些其它的事,像是「標記合併衝突(merge-conflicted)檔案為已解決」。

    比起「把這個檔案加進專案」,把它想成「把檔案內容加入下一個提交中」會比較容易理解。

Git 在執行 git add 命令時,會將當時的檔案內容預存起來。

如果 git add 後又對檔案做修改再提交,最後一次執行 git add 命令時,那個當下的版本會被提交,而不是在提交時你在工作目錄所看到的檔案版本被提交;

結論:如果在執行 git add 後修改檔案,必須再次執行 git add 預存最新版的檔案。


提交修改

git commit -m "commit message"任何未預存的檔案——新增的、已修改的,自從你編輯它們卻尚未用 git add 預存的——將不會納入本次的提交中; 它們仍以「已修改」的身份存在磁碟中。

git commit -am "commit message" 讓Git 在提交前自動預存所有已追蹤的檔案,略過輸入 git add


移除檔案

git rm 將檔案從預存區中移除,同時也將該檔案從工作目錄中移除。

若僅僅是將檔案從工作目錄中移除,那麼它會被列在 git status 輸出內容的「Changed but not updated」(也就是「未預存」)欄位下面:

$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    PROJECTS.md

no changes added to commit (use "git add" and/or "git commit -a")

接著執行 git rm,它會預存該檔案的移除動作:

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md

下一次提交時,該檔案將會消失而且不再被追蹤; 如果你修改了檔案且已經把修改內容加入索引中(「加入索引」 == 「預存」),必須使用 -f 選項才能強制將它移除,因為本次修改檔案的內容不會被 Git 保存而無法還原,只能回到上次提交的狀態,所做的保險機制。

git rm --cached <file> 保留工作目錄的檔案,但將它從預存區中移除。

可以將 files、directories、file-glob patterns 當成參數傳給 git rm

# 此命令會移除在 log/ 所有副檔名為 .log 的檔案。
# 星號 * 前面有反斜線(\); 這是必須的,因為 Git 在你的 Shell 檔名擴展(filename expansion)之上另外有自己的檔名擴展
$ git rm log/\*.log

移動檔案

$ git mv file_from file_to

相當於執行以下指令

$ mv file_from file_to
$ git rm file_from
$ git add file_to

Git 會在背後判斷檔案被重新命名,因此不管是哪個方法都沒差。


檢視提交的歷史記錄

git log 檢視之前發生過什麼事

options 說明
--until, --before 列出特定日期前的提交。
--author 列出作者名字符合指定字串的提交。
--committer 列出提交者名字符合指定字串的提交。
--grep 列出提交訊息中符合指定字串的提交。
-S 列出修改檔案中有加入或移除指定字串的提交。
-p 顯示每筆提交所做的修改內容。
-(n) 只輸出最後(n)筆提交內容。
-pretty= 以其它格式顯示提交。選項包括 oneline、short、full、fuller 及可自訂格式的 format。

Reference

Git Book


#Git







Related Posts

D42_檢討 W4 作業/W5 複習加寫心得

D42_檢討 W4 作業/W5 複習加寫心得

閉包

閉包

React 的 controller component 與 uncontrolled

React 的 controller component 與 uncontrolled


Comments