你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Git的分治之道

Git的分治之道

編輯:IOS開發基礎

Git-Logo-450x187.jpg

1、什麼是git

git的官方定義:Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.

可以看出,git是一個具有豐富命令集的版本控制系統,它的特點就是快速、可擴展以及分布式

而git實際上是一個簡單內容記錄工具(the stupid content tracker)。從根本上來講,git是一套內容尋址 (content-addressable) 文件系統,在此之上提供了一個版本控制系統的用戶界面。

1.1 an unusually rich command set

在當前版本裡,git一共有158個命令,但是常用命令只有10多個,其他命令可以視為底層命令。由於git最開始被設計的時候用來作為the stupid content tracker,只是現在被最廣泛地用來作為revision control system,但是實際上git也可以被用來做其它事情。因此,git不僅一開始提供了功能齊全的底層命令,而且還提供了許多友好的高層命令。

git命令分為兩種:

  • Procelain(高層命令)

user-friendly commands: init, add, commit, push, checkout, branch, merge, etc.

  • Plmbing(底層命令)

bunch of verbs that do low-level work: hash-object, update-index, write-tree, etc.

1.2 revision control system

版本控制系統,學名:software change and configuration management system(SCM)。它需要具有的特點如下:

  • 記錄和控制軟件的變化點和版本號

  • 能清楚知道什麼東西被改了,被誰改了

  • 在多人完成的大項目中,能更加容易的合作和管理文件

  • 最重要的是,要有回滾功能

在實際生活中,版本控制就被經常用到,例如寫論文時的版本控制。但是,這只是簡單的本地版本控制。在多人的項目合作中,這就顯得力不從心了。因為使用這種方法時,首先需要大量的拷貝工作,以保證能夠回滾嘛;其次當多人更改同一項目出現寫寫沖突情況時,雖然Linux提供diff和patch命令,但是解決起來肯定極其痛苦。

因此,版本控制系統,核心就是讓在不同系統上的開發者協同工作,需要解決的主要問題就是防止各種人一塊寫最後寫亂套的情況。

2、git有何不同

2.1 集中式版本控制系統

首先,談談傳統的集中式版本控制系統。

QQ截圖20160705152647.jpg

集中式版本控制系統中,版本庫是集中存放在中央服務器(server),每個開發者都作為客戶端(client)。協同工作的開發者都通過客戶端連到中央服務器,取出(checkout)最新的文件或者提交(commit)更新。它的主要特點如下:

  • 每次checkout拿到的都是當前版本庫最新的一個快照狀態(snapshot)。它並不知道版本庫之前的歷史狀態信息。

  • 每次commit都會更新中央版本庫。

  • 為了保證不丟失數據,在每次commit時都需要re-sync。

而集中式版本控制系統的缺點,則有如下幾點:

1. 對於每次commit操作,都需要re-sync;

對於集中式版本控制系統,在多人協作中,可以認為如果commit越早,麻  煩也就越少。

從這個角度上看,可以認為git做了兩步commit操作,第一次為commit,第二次為pull,只有pull才需要re-sync。

2. 單點故障

在沒有commit之前所有文件都在於client端的本地文件,如果此時不小心刪除掉了一些文件,除非重新訪問server進行回滾操作,否則就真的刪除掉

當server沒有備份並且宕掉了,就算能恢復當前版本的數據,但是之前的歷史數據信息是恢復不了的。

3. 每次工作的時候,需要聯網;

4. 速度慢;

大部分時間都是需要client和server進行溝通協作的。

2.2 分布式版本控制系統

我們再來看看git,它是一個分布式版本控制系統。

QQ截圖20160705152838.png

與傳統的集中式版本控制系統相比,git最大的不同在於,它是分布式版本控制系統。分布式意味著,不止服務器保存有version database,每個客戶端也保存有version database。git的主要特點如下:

  • 每次clone都真正的拷貝所有的數據。

  • 在初始clone之後,大多數的操作都是在本地進行的。

而git版本控制系統的主要優點如下三點:

1. Fast:

除了第一次clone的時候慢,之後的操作都比較快。因為大多數操作都是在本地進行的,不需要聯網。

2. Scalable:

你可以對本地的version database做任何你想做的事情,他並不會對其他人 造成任何影響。

3. Distributed:

在client端,肯定不法徹底避免單點故障,但是可以減少這樣可能性。因為本地保存有version database,所以當本地不小心丟失文件時,往往可以本地出錯本地解決。

在server壞掉的時候,可以根據client裡面的version database進行恢復,甚至可以把client當成一個新的server。

3、git如何存儲內部數據

從上一小節,我們知道git是一個分布式版本控制系統,無論是服務端還是客戶端都擁護一份自己的version database。那麼本小節,首先看看version database到底是什麼。然後,分別深入看看git init、git add、git commit這個三個常用命令具體的工作過程。

version database

git的version database到底是什麼?

1. 本質上,是一套內容尋址文件系統(Content Addressable Filesystem)。

2. 實現上,是一個簡單的KV數據庫

3. Key: sha-1 hash(everything is hashed)

  • 20 bytes, 40 hex, 160 bit, 2.9e48 distinct keys

  • How would  git handle a SHA-1 collision on a  blob?

4.Value: binary files

  • Commits : actual git commits

  • Trees : directories(structure of file system)

  • Blobs : contents of files/data

git以一種類似UNIX文件系統但更簡單的key-value方式來存儲內容。key是對文件內容的哈希值。value則包含有commit object、tree object以及blob object三種對象。

所有內容以tree或blob對象存儲,其中tree對象對應於UNIX中的目錄,blob對象則大致對應於inodes或文件內容。commit對象則可以看作是對當前版本的一個快照。

一個單獨的tree對象包含一條或多條tree記錄,每一條記錄含有一個指向blob或子tree對象的SHA-1指針,並附有該對象的權限模式、類型和文件名信息。

接下來一步一步,看來git flow裡到底發生了什麼:

3.1 git init命令

git init主要是創建了四個文件夾:.git、.git/refs/heads、.git/refs/tags以及.git/objects,和一個文件.git/HEAD,並把初始化./git/HEAD裡面的內容為指向master branch的HEAD。

那麼如何用普通的Linux命令實現git init呢?答案其實很簡單:

mkdir .git
mkdir -p .git/refs/heads
mkdir -p .git/refs/tags
mkdir -p .git/objects
touch .git/HEAD
echo "ref:refs/heads/master" > .git/HEAD

以上便是git init, without git init的具體實現做法。其中,objects目錄存儲所有數據內容,refs目錄存儲指向數據(分支)的提交對象的指針,HEAD文件指向當前分支。

3.2 git add命令

git add做了兩件事情:

第一,把文件寫成blob object作為value,並把文件內容的hash值作為key;

第二,更新了index文件,把文件放入了暫存區(staging area)。

echo "Hello World" > hello.txt
git add .

上面的操作可以用下面底層語句實現:

echo "Hello World" | git hash-object -w --stdin
git update-index --add --cacheinfo 100644 557db03de997c86a4028e1ebd3a1ceb225be238 hello.txt
git checkout -- hello.txt

第一個命令中,參數-w指示hash-object命令存儲(數據)對象,若不指定這個參數該命令僅僅返回鍵值。該命令輸出長度為40個字符的SHA-1哈希值校驗和,並創建以它的前2個字符為名稱的子目錄,剩下38個字符作為文件命名(保存至子目錄下)。

第二個命令,update-index為一個單獨文件創建一個index。更新完staging area後,由於僅僅Index裡有,但是本地仍沒有,如果git status,所以會顯示為deleted。此時,使用第三條命令可以把repository進行checkout出來。

3.3 git commit命令

我們進行第一次git commit操作。

git commit -m "First Commit"

此時會多出兩個文件。

第一個是commit object,它實際上是對於跟蹤項目內容的一次快照,裡面的格式內容有:指明了該時間點項目快照的頂層樹對象、作者/提交者信息以及提交注釋信息;

第二個則是那個被指向tree object,它實際上一個文件根目錄,裡面指向一個blob object,而這個blob object便是之前git add時的內容。

QQ截圖20160705153107.jpg

雖然執行git log命令可以查看完整的歷史信息並以此找到文件,那麼問題來了,系統又是如何找到commit log的呢? 答案是:refs!

在.git/HEAD裡存的是一個ref,ref是一個file,這個file裡面又存了一個commit的hash值,從而便可以找到了當前的base版本。

QQ截圖20160705153201.jpg

我們嘗試進行第二次git commit操作。 首先,git add一個簡單的腳本文件hello.sh。

/*************hello.sh的具體內容如下:

!/bin/sh
echo "Hello World"
**************/
git add hello.sh

此時,git add只會增加一個blob object,而不會增加多一個tree object。也就是說,在add的時候並不寫目錄,只是更新了暫存區,在index裡會保存目錄信息。因為在git commit操作前,可以進行多次git add操作。

git commit -m "Second Commit"

此時,會多出三個文件。第一個是commit object,第二個是根目錄的tree object,第三個是文件目錄的tree object。

通過下圖,可以發現commit object中還帶有個parent指針,它指向上一次的commit object。最重要的是,它還更新自身的refs/heads/master信息;

QQ截圖20160705153244.jpg

通過命令git ls-files --stage查看staging area信息。你還可以發現,暫存區裡只存儲blob object,並不存儲tree object和commit object。

4、branch/merge在git內部是如何工作

相對於git init、git add、git commit這三個命令,git branch和git merge則顯得十分的小巧機智。結合下列幾幅圖,能更輕松的理解這兩個命令。

4.1 branch操作

在git裡,branch操作,實際上僅僅增添一個41-bytes的引用文件(在refs/heads/目錄底下)。可以看出,這樣的操作是極其的簡單廉價的,示意圖如下:

QQ截圖20160705153323.jpg

同理,在git裡,checkout操作,實際上也僅僅是更改了引用文件的指針而已。

QQ截圖20160705153357.jpg

4.2 merge操作

最後,我們來分析一下,git裡的merge操作。它的操作流程和示意圖如下:

1. 計算出當前branch和公共branch的diff;

這步操作的速度很快。因為只需要去比較文件的hash值是否匹配即可。

2. 應用該diff去訂正本地文件;

這步操作並不會覆蓋分支裡之前的版本數據。

3. 解決所有的沖突;

QQ截圖20160705153435.jpg

當merge操作完成後,示意圖如下:

QQ截圖20160705153523.jpg

首先,可以發現,這是會新建一個mater處的commit object,而它有兩個parent指針,分別指向之前兩個branch的commit object。

其次,如果不小心做了一個bad merge,而又不想讓他人知道時,其實只需要更改下master和feature指針的hash信息即可。

最後,通過branch和merge操作,你會發現在git的世界裡,它會盡量不讓你做刪除的事情。

5、小結

Git is the stupid content tracker.

You can represent renames on top of git - git itself really doesn't care. In many ways you can just see git as a filesystem - it's content-addressable, and it has a notion of versioning, but I really really designed it coming at the problem from the viewpoint of a _filesystem_person (hey, kernels is what I do), and I actually have absolutely _zero_interest in creating a traditional SCM system. -- by Linus Torvalds

我們應該如何看待並理解git呢?本質上,git是一套文件尋址系統

雖然git具有版本控制的功能,但是在Linus最初設計git的時候,完完全全是從一個文件系統的角度進行設計和實現這個產品的。所以說,git本質上仍然是一個文件系統,只是它很適合地被用作版本控制系統。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved