git 入門教程之版本控制

雪之夢技術驛站發表於2019-03-18

版本控制

我們知道 git 是分散式版本控制系統,所以稱被控制物件是版本本身沒錯,但是從git 命令中發現,並沒有版本這個名詞,有的只是commit,所以前幾節我一直稱其為提交.

為了避免後續教程引發歧義,特意說明,無論是版本也好,提交也罷,都是中文翻譯而已,不必太過較真,直接原汁原味稱commit也可以啊!

假設你已掌握暫存區的相關概念,簡單來說,暫存區就是更改檔案的快取集合,等待一次性全部提交到版本庫,正因如此,方便我們批量操作相關性檔案,打包提交到版本庫,這正是暫存區的獨特魅力.

我們反覆在說 git 是分散式版本控制系統,分散式的概念已經粗略講過多次了,下面我們講一下版本控制,談談 git 的版本控制和其他系統的版本控制有什麼不同,為什麼 git 這麼優秀,如此流行?

git 跟蹤並管理的是更改,而非檔案本身.正如linux 一切皆檔案,java 一切皆物件一樣,git 一切皆更改.新增檔案是一個更改,新增檔案內容是一個更改,修改檔案內容是一個更改,刪除檔案內容也是一個更改,換言之,git 管理的正是這一個個的更改,並不是檔案本身.

下面我們用事實說話,證明 git 管理的是更改而不是檔案本身:

第一步,追加 git tracks changestest.txt 檔案

# 檢視 test.txt 檔案內容
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
# 追加 git tracks changes 檔案內容到 test.txt 檔案
$ echo "git tracks changes" >> test.txt
# 再次檢視 test.txt 檔案內容
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes
$ 
複製程式碼

第二步,新增test.txt 檔案到暫存區並檢視檔案狀態

$ git add test.txt
sunpodeMacBook-Pro:demo sunpo$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

$ 
複製程式碼

對於上述內容應該不必再解釋了吧,無外乎說test.txt 檔案已修改(modified),即將被提交(to be committed).

但是,此時偏偏不提交,繼續修改 test.txt 檔案:(這種情況實際工作中也有可能出現,比如你正在研發某功能,本以為已經開發完畢,滿心歡喜新增到暫存區,然後意外發現一個小bug,分分鐘就修復了,時間間隔很短以至於你根本不記得還需要再次新增到暫存區.)

第三步,繼續修改檔案內容,忘記再次新增到暫存區

# 編輯 test.txt 檔案,將 git tracks changes 更改為 git tracks changes of files
vim test.txt
# 檢視 test.txt 檔案內容
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
$ 
複製程式碼

第四步,正常提交暫存區的全部更改到版本庫

$ git commit -m "git tracks changes"
[master 2daa74a] git tracks changes
 1 file changed, 1 insertion(+)
複製程式碼

此次提交後,我們再看一下檔案狀態:

$ git status
On branch master
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:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

no changes added to commit (use "git add" and/or "git commit -a")
$
複製程式碼

發現有什麼不同嗎?以往提交後再次檢視檔案狀態,工作區都是乾淨的,這次居然提示我們 test.txt 檔案已經修改但未新增到暫存區?!

等一下,我們先回憶一下我們的操作流程:

第一次修改(git tracks changes) -> git add -> 第二次修改(git tracks changes of files) -> git commit

這樣就很好理解了,git 管理的是更改而不是檔案本身,如果是檔案本身的話,應該將檔案的內容全部提交才對,所以管理的是更改.

第一次修改過後使用 git add 命令將工作區的第一次修改內容放到暫存區準備提交,但是此時工作區發生了第二次修改,注意,這次修改並沒有放到暫存區,所以下一步的git commit 命令提交的暫存區內容中自然也就沒有第二次修改的內容了!所以git commit 完畢後執行git status命令才會發現此時工作區和暫存區還存在版本差異,即此時工作區不是乾淨的!

這一次的實驗很好理解,工作區的修改需要主動告訴暫存區,暫存區的全部更改再提交到版本庫.所以版本庫的提交取決於暫存區,而暫存區又取決工作區是否主動將更改新增進去了嗎!

理論再多不如親身體驗,讓我們直接比較一下工作區和版本庫的差異吧!

# 比較 test.txt 檔案在工作區和版本庫的差異
$ git diff HEAD -- test.txt
diff --git a/test.txt b/test.txt
index d31bdd2..56c76b7 100644
--- a/test.txt
+++ b/test.txt
@@ -3,4 +3,4 @@ git init
 git diff
 understand how git control version
 how git work
-git tracks changes
+git tracks changes of files
$ 
複製程式碼

由此可見,工作區比版本庫多了git tracks changes of files,少了git tracks changes,所以說第二次修改內容 git tracks changes of files 並沒有被提交.

現在我們再解釋一下-git tracks changes+git tracks changes of files 的問題:

首先檢視工作區 test.txt 檔案內容

$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
$ 
複製程式碼

根據上述分析,我們知道第一次的修改git tracks changes 已被提交到版本庫,第二次的修改git tracks changes of files 沒有被提交而是繼續留在工作區.

因此,可以推斷出目前版本庫的檔案應該是這樣的:

git test
git init
git diff
understand how git control version
how git work
git tracks changes
複製程式碼

既然如何,工作區和版本庫相比豈不剛好是少了一個git tracks changes,多了git tracks changes of files,其餘檔案內容完全相同!

透過現象看本質,已經分析了現象也解釋了產生現象的原因,是時候分析一下本質了.

丟擲問題:因為git tracks changes of fielsgit tracks changes 被視為不同的更改,所以才會造成上述現象.如果git tracks changes of fiels 被認為是git tracks changes + of fiels 兩者疊加產生的更改,還會產生上述現象嗎?

答案是否定的,如果兩個更改可以疊加的話,按照版本控制的思路,第二次的修改即便沒有提交也只是 of fiels 沒有加入到版本庫而已,如此一來,工作區和版本庫的差異將不再是少了一個git tracks changes,多了git tracks changes of files,而僅僅是多了of files!

由此可見,git 版本控制系統其實是全量更新的思維模式,並不是差量更新模式.

小結

  • 工作區的更改需要git add 新增到暫存區,git commit 將暫存區的全部更改提交到版本庫.
  • 工作區,暫存區,版本庫三者既相關獨立又密切關聯,三者是傳遞性依賴的關係.
  • git 版本控制的是檔案的更改,而不是檔案本身,是全量更新模式,而不是差量更新模式.

相關文章