git工作原理

Git工作原理

時間 2016-11-05 23:31:36  Simeone

原文  http://deweixu.me/2016/11/05/how-git-works/

主題 Git

最近在使用git時遇到了一些問題,用google搜索到一篇很好的文章,簡單的翻譯了一下。(原文地址: https://codewords.recurse.com/issues/two/git-from-the-inside-out )

這篇文章解釋了 git 的工作原理,它會使你更深入的理解 git ,更好的使用它來控制項目的版本。

本文重點介紹了支持Git的圖形結構,以及該圖形的屬性指示Git行爲的方式。從基礎開始,同時有實例講解,根據實例建立一個更真實的模型,讓你更好地理解 git 做了什麼。

創建項目

~ $ mkdir alpha
~ $ cd alpha

項目目錄是 alpha

~/alpha $ mkdir data~/alpha $ printf 'a' > data/letter.txt

到目錄 alpha 下創建了一個名爲 data 的目錄,在裏面創建了一個名爲 letter.txt 的文件,其中的內容是一個字符 a , alpha 目錄結構如下:

alpha└── data
 └── letter.txt

初始化倉庫

~/alpha $ git init
 Initialized empty Git repository

git init 使當前目錄變成了 Git 倉庫,爲此,它創建了一個 .git 目錄並向其中寫入了一些文件。這些文件定義了關於Git配置和項目歷史的一切,它們只是普通文件。 用戶可以使用文本編輯器或shell來讀取和編輯它們。 這就是說,用戶可以像他們的項目文件一樣輕鬆地閱讀和編輯他們項目的歷史。

現在 alpha 目錄的結構就像下面這樣

alpha├── data| └── letter.txt
└── .git
 ├── objects
 etc...

.git 目錄及其內容歸 Git 系統所有,所有其他的文件統稱爲工作副本,歸用戶所有。

添加文件

~/alpha $ git add data/letter.txt

運行上面的命令,有兩個效果。

首先,它在 .git/objects/ 目錄中創建了一個新的 blob 文件。

這個 blob 文件包含 data/letter.txt 的壓縮內容。 它的名稱通過文件內容的 Hash (應該是用的 sha1 )得到。取一段文本的 Hash 值意味着運行一個程序,將其內容變成一塊較小的文本,這塊文本是原始內容的唯一標識。例如, Git 將 aes 轉換爲 2e65efe2a145dda7ee51d1741299f848e5bf752e ,前兩個字符用作對象數據庫中的目錄的名稱: .git/objects/2e/ 。 散列的其餘部分用作保存所添加文件的內容的 blob 文件的名稱:

.git/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e 。

git add 將文件添加到 Git 並將其內容保存到 objects 目錄中。 如果用戶從工作副本中刪除 data/letter.text ,它的內容在 Git 中仍然是安全的。

其次, git add 將文件添加到 索引 。 索引 是一個列表,其中包含 Git 已被告知要跟蹤的每個文件。 它作爲一個文件存儲在 .git/index 。 文件的每一行將被跟蹤的文件映射到其內容的 哈希 。 這是運行 git add 命令後的索引:

data/letter.txt 2e65efe2a145dda7ee51d1741299f848e5bf752e

創建一個包含內容 1234 的文件 data/number.txt

~/alpha $ printf '1234' > data/number.txt

目錄結構變成了下面這樣:

alpha└── data
 └── letter.txt
 └── number.txt

添加文件到 Git

~/alpha $ git add data

git add 命令創建一個包含 data/number.txt 內容的 blob 對象。 它爲指向 blob的 data/number.txt 添加一個索引條目。 這是 git add 命令第二次運行後的索引:

data/letter.txt 2e65efe2a145dda7ee51d1741299f848e5bf752edata/number.txt 274c0052dd5408f8ae2bc8440029ff67d79bc5c3

只有數據目錄中的文件被列在索引中,雖然用戶運行了 git add data 。 數據目錄data 不單獨列出。

~/alpha $ printf '1' > data/number.txt
~/alpha $ git add data

當最初創建 data/number.txt 時,想要輸入內容 1 ,而不是 1234 .他們進行更正並將文件再次添加到索引。 此命令將使用新內容創建一個新的 blob 。 並且它更新 data/number.txt 的索引條目以指向新的 blob 。

git commit

~/alpha $ git commit -m 'a1'
 [master (root-commit) 774b54a] a

進行 a1 提交, Git 打印了這次提交的相關信息。

commit 命令有三個步驟。 創建一個樹形圖來表示正在提交的項目版本的內容。 創建一個提交對象。 將當前分支指向新的提交對象。

創建樹形圖

Git 通過從索引創建樹圖來記錄項目的當前狀態。 此樹圖記錄項目中每個文件的位置和內容。

該圖由兩種類型的對象組成: blob 和  。

Blob 是通過 git add 存儲的。 它們表示文件的內容。

在 commit 時存儲樹。 樹表示工作副本中的目錄。

下面是記錄新提交的 data 目錄的內容的樹對象:

100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt100664 blob 56a6051ca2b02b04ef92d5150c9ef600403cb1de number.txt

第一行記錄展示了 data/letter.txt 文件的信息。 第一部分是文件的權限。 第二部表示此條目的內容由 blob 而不是  表示。 第三部分描述了 blob 的 Hash 。 第四部分描述文件的名稱。第二行當然就是文件 data/number.txt 文件的信息。

下面是 alpha 的樹對象:

040000 tree 0eed1217a2947f4930583229987d90fe5e8e0b74 data

alpha 樹對象只包含了一個指向 data 樹指針。(譯著:如果 alpha 目錄下還有一個文件, alpha 樹對象就還會多一行,就是指向多出文件的 blob 對象)

BzMnMvF.png!web

在上面的圖中, root 樹指向 data 樹。 data 樹指向 data/letter.txt 和 data/number.txt 的 blob 。

創建一個提交對象

git commit 在創建樹圖後創建一個 提交對象 。 提交對象 只是 .git/objects/ 中的另一個文本文件:

tree ffe298c3ce8bb07326f888907996eaa48d266db4
author Mary Rose Cook <[email protected]> 1424798436 -0500committer Mary Rose Cook <[email protected]> 1424798436 -0500a1

第一行指向樹圖。 Hash 是表示工作副本的根的樹對象。 也就是 alpha 目錄。 最後一行是提交消息。

ABVzqaQ.png!web

將當前分支指向新的提交

最後,commit命令將當前分支指向新的提交對象。哪個是當前分支? .git/HEAD 文件記錄了當前分支:

ref: refs/heads/master

這說明 HEAD 指向 master , master 是主分支。

HEAD 和 master 都是 refs 。 ref 是 Git 用來標識特定提交的標籤。

表示 master 引用的文件不存在,因爲這是對倉庫的第一次提交。 Git 在 .git/refs/heads/master 下創建文件,並將其內容設置爲提交對象的哈希值:

74ac3ad9cde0b265d2b4f1c778b283a6e2ffbafd

(如果你在閱讀時輸入這些 Git 命令,你的 a1 提交的 哈希值 將不同於我的 哈希值 。 內容對象(如blob和樹)總是散列爲相同的值。 提交不會,因爲它們包括創建者的日期和名稱。)

添加 HEAD 和 master 到樹圖:

2umMfau.png!web

HEAD 指向 master ,就像提交之前一樣。 但 maste r現在存在並指向新的提交對象a1 。

再一次commit

下面是 a1 提交後的 Git 結構圖。 包含工作副本和索引。

mauARju.png!web

工作副本,索引和 a1 提交都具有與 data/letter.txt 和 data/number.txt 相同的內容。 索引和 HEAD 提交都使用 Hash 來引用 blob 對象,但是工作副本內容作爲文本存儲在不同的地方。

~/alpha $ printf '2' > data/number.txt

將 data/number.txt 的內容設置爲 2 .這會更新工作副本,但 索引 和 HEAD 不變。

yuiIbqv.png!web

~/alpha $ git add data/number.txt

將文件添加到 Git 。 這會向 objects 目錄添加一個包含 2 的 blob 。 它指向新blob 的 data/number.txt 的索引條目。

qaQfMv2.png!web

~/alpha $ git commit -m 'a2'
 [master f0af7e6] a2

提交的步驟與之前相同。

首先,創建一個新的樹形圖來表示索引的內容。

data/number.txt 的索引條目已更改。 舊的數據樹不再反映 data 目錄的索引狀態。 必須創建一個新的 data 樹對象:

100664 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e letter.txt100664 blob d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 number.txt

新數據樹與舊數據樹的哈希值不同。 必須創建一個新的根樹以記錄此 Hash 值:

040000 tree 40b0318811470aaacc577485777d7a6780e51f0b data

其次,創建一個新的提交對象。

tree ce72afb5ff229a39f6cce47b00d1b0ed60fe3556
parent 774b54a193d6cfdd081e581a007d2e11f784b9fe
author Mary Rose Cook <[email protected]> 1424813101 -0500committer Mary Rose Cook <[email protected]> 1424813101 -0500a2

提交對象的第一行指向新的根樹對象。 第二行指向 a1 :新提交的父級。要找到父提交,要跟着 HEAD 和 master 來掌握並發現 a1 的提交哈希。

最後,master分支文件的內容被設置爲新提交的 hash 值。

u6Jz2i3.png!web

EZzMjqi.png!web

內容存儲爲對象樹。 這意味着只有diffs存儲在對象數據庫中。 看看上面的圖表。 a2 commit 重用了在 a1 提交之前創建的 blob 。 類似地,如果提交中整個沒有變,則其樹以及其下的所有 blob 和樹可以被重用。 一般來說,提交的內容更改很少。 這意味着 Git 可以在小的空間中存儲大的提交歷史。

每個提交都有一個父級。 這意味着存儲庫可以存儲項目的歷史記錄。

refs 是提交歷史的一部分或另一部分的入口點。 這意味着提交可以被賦予有意義的名稱。 用戶將他們的工作組織到對他們的項目有意義的譜系中,具體的參考如 fix-for-bug-376 。 Git 使用符號引用,如 HEAD , MERGE_HEAD 和 FETCH_HEAD 來支持操作提交歷史記錄的命令。

objects 目錄中的節點是不可變的。 這意味着內容被編輯,而不是被刪除。 每一次添加的內容和每次提交的對象都是在目錄中.

refs 是可變的。 因此, ref 的含義可以改變。 master 指向的提交可能是當前項目的最佳版本,但是,很快,它將被更新的更好的提交取代。

Check out a commit

~/alpha $ git checkout 37888c2
 You are in 'detached HEAD' state...

使用 Hash 值 checkout``a2 的提交(如果你在運行這些git命令,這裏的 hash 值要換成你自己的,使用 git log 查看)

checkout 有四個步驟:

  • 獲取 a2 提交,並獲取指向它的樹圖

  • 它將樹形圖中的文件條目寫入工作副本。 這將導致沒有更改。 工作副本已經具有被寫入其中的樹圖的內容,因爲 HEAD 已經通過 master 指向 a2 提交。

  • 將樹圖中的文件條目寫入索引。 這也導致沒有變化。 索引已經具有 a2 提交的內容。

  • HEAD 的內容設置爲 a2 提交的哈希:

f0af7e62679e144bb28c627ee3e8f7bdb235eee9

將 HEAD 的內容設置爲 Hash 值會使存儲庫處於分離的 HEAD 狀態。 注意在下面的圖表中, HEAD 直接指向 a2 提交,而不是指向 master 。

ZFFrYzy.png!web

~/alpha $ printf '3' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'a3'
 [detached HEAD 3645a0e] a3

將 data/number.txt 的內容設置爲 3 ,並提交更改。 Git 去 HEAD 得到 a3 提交的父級。 而不是找到一個分支ref,它找到並返回 a2 提交的哈希。

Git 更新 HEAD 直接指向新的 a3 提交的哈希。 存儲庫仍處於分離的 HEAD 狀態。 它不在一個分支上,因爲沒有提交指向 a3 或其一個後代。 這意味着它很容易丟失。

aYBba2i.png!web

創建分支

~/alpha $ git branch deputy

創建一個新分支 deputy 。 這只是在 .git/refs/heads/deputy 創建一個新文件,其中包含 HEAD 指向的哈希, 也就是 a3 提交的哈希。

分支只是refs, refs只是文件。 這意味着Git分支是輕量級的。

deputy 分支的創建將新的 a3 提交安全地放置在分支上。 HEAD 仍然分離,因爲它仍然直接指向一個提交。

RvYnquV.png!web

切換分支

~/alpha $ git checkout master
 Switched to branch 'master'

切換到了 master 分支

  • 獲取 a2 提交,並將 master 指向獲取提交點的樹圖。

  • 樹形圖中的文件條目替換工作副本的文件。 這將使 data/number.txt 的內容設置爲2。

  • 將樹圖中的文件條目寫入索引。 這會將 data/number.txt 的條目更新爲2個 blob的散列。

  • 改變 HEAD 的值

ref: refs/heads/master

ueyUfiA.png!web

切換到與工作副本不兼容(有改變)的分支

~/alpha $ printf '789' > data/number.txt
~/alpha $ git checkout deputy
 Your changes to these files would be overwritten
 by checkout:
 data/number.txt
 Commit your changes or stash them before you
 switch branches.

將 data/number.txt 的內容設置爲 789 , 當 checkout 到 deputy 時, Git 報了一個錯誤。

HEAD 指向 master , master 指向 a2 ,其中 data/number.txt 的內容是 2 。 deputy 指向 a3 ,其中 data/number.txt 的內容是 3 。 data/number.txt 在工作副本的內容爲 789 ,所有這些版本都不同,差異必須解決。

Git 可以使用要切換分支中提交的版本替換掉工作副本中的版本,這樣可以避免數據丟失。

Git 可以合併工作副本的版本和要切換分支中的版本,但這很複雜。

所以 Git 報了一個錯誤,不能切換分支。

~/alpha $ printf '2' > data/number.txt
~/alpha $ git checkout deputy
 Switched to branch 'deputy'

把 data/number.txt 的內容變回 2 時,便切換成功了。

YZBbYjB.png!web

合併祖先

~/alpha $ git merge master
 Already up-to-date.

將主分支 master 和併到 deputy 分支。和並兩個分支實際上是合併兩個提交。第一個提交指向 deputy ,它是接收者。第二個提交指向 master ,它是提交者。可以理解爲把 master 提交到 deputy 。對於這個合併, git 什麼也沒有做,因爲兩個分支的內容是一樣的。

圖中的一系列的提交可以看成是對存儲庫的一系列更改。這也就意味着,在合併中,如果提交者( master )是接收者( deputy )的祖先, git 將什麼也不做,因爲這些變化已經存在。

合併後代

~/alpha $ git checkout master
 Switched to branch 'master'

切換到分支 master

2iuUBna.png!web

~/alpha $ git merge deputy Fast-forward

合併 deputy 到 master 。 Git 發現接受者的 a2 提是提交者 a3 的祖先,它可以做快進合併。

獲得提交者的提交 a3 並提供指向它的樹圖,將樹圖中的文件條目寫入工作副本和索引。 快進 是指 master 指向 a3 。

UZbuYva.png!web

在合併中,如果提交者( deputy 分支上的 a3 提價)是接收者( master 上的 a2 提價)的後代,則歷史記錄不改變。 已經有一系列提交描述了要做出的改變( 接收者和提交者之間的提交序列 )。 雖然Git歷史沒有改變,Git圖確實改變。 HEAD指向的具體引用被更新爲指向提交者( master 指向 a3 )。

合併來自兩個不同譜系的分支

~/alpha $ printf '4' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'a4'
 [master 7b7bd9a] a4

把 number.txt 的內容設置爲 4 ,並提交到 master

~/alpha $ git checkout deputy Switched to branch 'deputy'~/alpha $ printf 'b' > data/letter.txt
~/alpha $ git add data/letter.txt
~/alpha $ git commit -m 'b3'
 [deputy 982dffb] b3

切換到 deputy 分支,把 data/letter.txt 的內容設置爲 b ,並提交到 deputy 。

7FfaQbr.png!web

提交可以共享父級,這意味着可以在提價的歷史中創建新的譜系。

提交可以有多個父級。 這意味着單獨的譜系可以通過具有兩個父的提交來合併:合併提交。

~/alpha $ git merge master -m 'b4'
 Merge made by the 'recursive' strategy.

合併 master 到 deputy

Git 發現接收者 b3 和提供者 a4 在不同的譜系中。 它做一個合併提交。 這個過程有八個步驟。

  1. Git 將提交者的提交的哈希寫入到 alpha/.git/MERGE_HEAD 文件。 這個文件的存在告訴 Git 在合併中。

  2. Git 查找基本提交:接收者和提交者提交的最近的祖先的共同點。

7NJnaiN.png!web

提交有父級別。 這意味着可以找到兩個譜系起始點。 Git 從 b3 向後跟蹤,找到所有的祖先,從 a4 向後尋找所有的祖先。 它找到兩個譜系共享的最近的祖先 a3 。 這是基本提交。

  1. Git 從接收者和提交者提交的樹圖生成基本的索引。

  2. Git 生成一個 diff ,它將接收者提交和提交者提交對基礎提交所做的更改合併。 此 diff 是指向更改的文件路徑列表:添加,刪除,修改或衝突。

Git 獲取出現在 base , receiver 或 giver 索引中的所有文件的列表。 比較較索引條目以決定對文件做出的更改。 它將一個相應的條目寫入 diff 。 在這種情況下, diff 有兩個條目。

第一個條目是是 data/letter.txt 。 文件內容在 base 和 receiver 中不同。 但是在 base 和 giver 中是一樣的。 Git 看到內容被 reviceer 修改,但是沒有被 giver 修改。 data/letter.txt 的 diff 條目是一個修改,而不是衝突。

diff 中的第二個條目是 data/number.txt 。 在這種情況下,文件內容在 base 和receiver 中是相同的,並且在 giver 中是不同的。 data/letter.txt 的 diff 條目也是一個修改。

可以找到合併的 base 提交。 這意味着,如果一個文件只是從 receiver 或提 giver 的 base 改變, Git 可以自動解決該文件的合併。 這減少了用戶必須做的工作。

  1. 由 diff 中的條目指示的更改將應用於工作副本。 data/letter.txt 的內容設置爲 b , data/number.txt 的內容設置爲 4 。

  2. 由 diff 中的條目指示的更改將應用於索引。 data/letter.txt 的條目指向 b blob , data/number.txt 的條目指向 4 blob 。

  3. 更新索引:

tree 20294508aea3fb6f05fcc49adaecc2e6d60f7e7d
parent 982dffb20f8d6a25a8554cc8d765fb9f3ff1333b
parent 7b7bd9a5253f47360d5787095afc5ba56591bfe7
author Mary Rose Cook <[email protected]> 1425596551 -0500committer Mary Rose Cook <[email protected]> 1425596551 -0500b4

注意:這次提交有兩個父級

  1. 將當前分支 deputy 分支指向新的提交。

6bMvU3M.png!web

合併來自不同譜系的兩個提交,這兩個提交都修改同一個文件

切換到 master 分支,並把 deputy 合併到 master , 快進 到 b4 ,現在 master和 deputy 都指向同一個提交

6vmq6jv.png!web

~/alpha $ git checkout deputy
 Switched to branch 'deputy'~/alpha $ printf '5' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'b5'
 [deputy bd797c2] b5

切換到 deputy 分支,把 data/number.txt 的內容設置爲 5 ,並提交。

~/alpha $ git checkout master
 Switched to branch 'master'~/alpha $ printf '6' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m 'b6'
 [master 4c3ce18] b6

切換到 master 分支,把 data/number.txt 的內容設置爲 6 ,並提交。

6Nz2uif.png!web

~/alpha $ git merge deputy
 CONFLICT in data/number.txt Automatic merge failed; fix conflicts and commit the result.

將 deputy 合併到 master 。存在衝突,並且合併已暫停。衝突合併的過程遵循與未衝突合併的過程相同的前六個步驟:設置 .git/MERGE_HEAD ,查找 base ,生成 base , receiver , giver 的索引,創建 diff ,更新工作副本和更新索引。由於衝突,不採取第七提交步驟和第八更新 ref 步驟。讓我們再次看看這些步驟,發生了什麼。

  1. Git 將 giver 提交的哈希寫入 .git/MERGE_HEAD 文件。

BJni63y.png!web

  1. Git 找到 base 提交 b4

  2. Git 從接收者和提交者提交的樹圖生成基本的索引。

  3. Git 生成一個 diff ,它將接收者提交和提交者提交對基礎提交所做的更改合併。 此 diff 是指向更改的文件路徑列表:添加,刪除,修改或衝突。

在這種情況下, diff 只包含一個條目: data/number.txt 。 該條目被標記爲衝突,因爲 data/number.txt 的內容在接收者,提供者和 base 中是不同的。

  1. 由 diff 中的條目指示的更改將應用於工作副本。 對於衝突區域, Git 將兩個版本寫入工作副本中的文件。 data/number.txt 的內容設置爲:

<<<<<<< HEAD6=======5>>>>>>> deputy
  1. 由 diff 中的條目指示的更改應用於索引。 索引中的條目通過其文件路徑和階段的組合成唯一標識。 未衝突文件的條目具有階段 0 .在此合併之前,索引如下所示,其中 0 是階段值:

0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea7480 data/number.txt 62f9457511f879886bb7728c986fe10b0ece6bcb

在合併 diff 被寫入索引之後,索引如下所示:

0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea7481 data/number.txt bf0d87ab1b2b0ec1a11a3973d2845b42413d97672 data/number.txt 62f9457511f879886bb7728c986fe10b0ece6bcb3 data/number.txt 7813681f5b41c028345ca62a2be376bae70b7f61

在階段 0 的 data/letter.txt 的條目與在合併之前相同。 在階段 0 的 data/number.txt 的條目被去掉了。 它有三個新的條目。 階段 1 的條目具有 base data/number.txt 內容的散列。 階段 2 的條目具有 receiver data/number.txt 內容的散列。 階段 3 的條目具有 giver data/number.txt 內容的散列。 這三個條目的存在告訴 Git data/number.txt 是衝突的。合併就暫停了。

~/alpha $ printf '11' > data/number.txt
~/alpha $ git add data/number.txt

通過將 data/number.txt 的內容設置爲 11 來合成兩個衝突版本的內容。他們將文件添加到索引。 Git 添加一個包含 11 的 Blob 。添加一個衝突的文件告訴 Git 衝突已解決。 Git 從索引中刪除階段 1 , 2 和 3 的 data/number.txt 條目。 在階段0 的 data/number.txt 的條目中添加新 blob 的散列。 該索引現在爲:

0 data/letter.txt 63d8dbd40c23542e740659a7168a0ce3138ea7480 data/number.txt 9d607966b721abde8931ddd052181fae905db503
~/alpha $ git commit -m 'b11'
 [master 251a513] b11
  1. 提交。 Git 在存儲庫中看到 .git/MERGE_HEAD ,告訴它合併正在進行。 然後檢查索引並發現沒有衝突。 就創建一個新的提交 b11 ,以記錄解析的合併的內容。 z最後會刪除 .git/MERGE_HEAD 文件。 這將完成合並。

  2. 將當前分支 master 指向新的提交。

JfqUfi2.png!web

移除一個文件

下面的圖包括提交歷史、最近提交的樹和 blob 以及工作副本和索引:

auaQZnA.png!web

~/alpha $ git rm data/letter.txt
 rm 'data/letter.txt'

告訴 Git 刪除 data/letter.txt 。 該文件從工作副本中刪除。 該條目從索引中刪除。

7jeYRr.png!web

~/alpha $ git commit -m '11'
 [master d14c7d2] 11

提交。 作爲提交的一部分,一如既往,Git構建一個表示索引內容的樹形圖。 data/letter.txt 不包括在樹圖中,因爲它不在索引中。

VZZ7jaE.png!web

複製存儲庫

~/alpha $ cd ..
 ~ $ cp -R alpha bravo

將 alpha/ 存儲庫的內容複製到 bravo/ 目錄。 這將產生以下目錄結構:

~
├── alpha
| └── data| └── number.txt
└── bravo
 └── data
 └── number.txt

現在 bravo 目錄中有另一個 Git 圖:

UNBjQrn.png!web

將存儲庫鏈接到另一個存儲庫

 ~ $ cd alpha
~/alpha $ git remote add bravo ../bravo

移回到 alpha 存儲庫。 他們將 bravo 設置爲 alpha 上的遠程存儲庫。 這會在 alpha/.git/config 文件中添加:

[remote "bravo"]
 url = ../bravo/

從遠程獲取分支

~/alpha $ cd ../bravo
~/bravo $ printf '12' > data/number.txt
~/bravo $ git add data/number.txt
~/bravo $ git commit -m '12'
 [master 94cd04d] 12

進入 bravo 存儲庫。 將 data/number.txt 的內容設置爲 12 ,並將更改提交到 bravo 上的 master 。

vQzAv2y.png!web

~/bravo $ cd ../alpha
~/alpha $ git fetch bravo master
 Unpacking objects: 100% From ../bravo
 * branch master -> FETCH_HEAD

進入 alpha 存儲庫。 從 bravo 獲取 master 到 alpha 。 這個過程有四個步驟。

  1. Git 獲取 master 在 bravo 上指向的提交的哈希。 這是 12 提交的哈希。

  2. Git 提供了 12 提交所依賴的所有對象的列表:提交對象本身,其樹圖中的對象,12 提交的祖先提交和它們的樹圖中的對象。 它從此列表中刪除 alpha 對象數據庫已有的對象。 它將其餘部分複製到 alpha/.git/objects/ 。

  3. 將 alpha/.git/refs/remotes/bravo/master 下的具體 ref 文件的內容設置爲12提交的哈希值。

  4. 將 alpha/.git/FETCH_HEAD 的內容設置爲:

94cd04d93ae88a1f53a4646532b1e8cdfbc0977f branch 'master' of ../bravo

下圖表示了 fetch 命令從 bravo 獲取了 master 的 12 提交

mQ7naar.png!web

對象是可以複製的,這意味着可以在存儲庫之間共享歷史記錄。

存儲庫可以存儲遠程分支引用,如 alpha/.git/refs/remotes/bravo/master , 這意味着存儲庫可以在本地記錄在遠程存儲庫上分支的狀態。 在獲取時是正確的,但如果遠程分支改變,它將過期。

合併FETCH_HEAD

~/alpha $ git merge FETCH_HEAD
 Updating d14c7d2..94cd04d Fast-forward

合併 FETCH_HEAD , FETCH_HEAD 只是另一個 ref 。 解析了 12 提交, giver 。 master 開始指向 11 提交。 Git 做一個快進合併,並將 master 指向在 12 提交。

2ieqEnm.png!web

從遠程分支Pull

~/alpha $ git pull bravo master
 Already up-to-date.

將 bravo 的 master 拉到 alpha 。 Pull 是 fetch and merge FETCH_HEAD 的縮寫。

Clone一個存儲庫

~/alpha $ cd ..
 ~ $ git clone alpha charlie
 Cloning into 'charlie'

移動到上面的目錄。 clone alpha 到 charlie 。 clone 到 charlie 具有與生成bravo 存儲庫的 cp 類似的結果。 Git 創建一個名爲 charlie 的新目錄。 它將 charlie 作爲一個Git倉庫,將 alpha 添加爲遠程倉庫被稱爲 origin ,獲取源併合並 FETCH_HEAD 。

Push分支到遠程分支

 ~ $ cd alpha
~/alpha $ printf '13' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m '13'
 [master 3238468] 13

返回到 alpha 倉庫,把 data/number.txt 的內容設置爲 13 ,並提交。

~/alpha $ git remote add charlie ../charlie

設置 alpha 的遠程倉庫爲 charlie

~/alpha $ git push charlie master
 Writing objects: 100%
 remote error: refusing to update checked out
 branch: refs/heads/master because it will make
 the index and work tree inconsistent

push master 到 charlie .

13 提交所需的所有對象都複製到 charlie 。

此時,推送過程停止。 Git 告訴我們出了什麼問題。 它拒絕推送到遠程分支。 這是有道理的, 因爲推送將更新遠程索引和 HEAD 。 這將導致混亂,如果有人正在編輯遠程的工作副本。(這也有其他的解決辦法,可以google一下)

此時,可以創建一個新的分支,將 13 提交合併到其中,並將該分支推送到 charlie 。但是我們想要一個類似 GitHub 那樣的中央倉庫,無論什麼時候都可以 push pull 。(中央倉庫爲什麼可以?因爲在初始化倉庫的時候使用的是 git init --bare , 初始化成一個裸存儲庫,遠程倉庫應該都要這麼初始化。)

Clone 一個裸倉庫

~/alpha $ cd ..
 ~ $ git clone alpha delta --bare
 Cloning into bare repository 'delta'

移動到上面的目錄。 將 delta clone 爲裸存儲庫。 這是一個有兩個區別的普通 clone 。 配置文件指示存儲庫是裸的。 通常存儲在 .git 目錄中的文件存儲在存儲庫的根目錄中如下:

delta├── HEAD
├── config
├── objects
└── refs

zeUzEzu.png!web

Push分支到裸存儲庫

 ~ $ cd alpha
~/alpha $ git remote add delta ../delta

返回到 alpha 存儲庫。 將 delta 設置爲 alpha 上的遠程存儲庫。

~/alpha $ printf '14' > data/number.txt
~/alpha $ git add data/number.txt
~/alpha $ git commit -m '14'
 [master cb51da8] 14

將 data/number.txt 的內容設置爲 14 ,並將更改提交到 alpha 上的 master

36Fb2m3.png!web

~/alpha $ git push delta master
 Writing objects: 100%
 To ../delta 3238468..cb51da8 master -> master

push master 到 delta ,有3個步驟

  1. master 分支上的 14 提交所需的所有對象都從 alpha/.git/objects/ 複製到 delta/objects / 。

  2. delta/refs/heads/master 被更新爲指向 14 提交。

  3. alpha/.git/refs/remotes/delta/master 設置爲指向 14 提交。 alpha 具有 delta 的狀態的最新記錄.




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