由於作操做系統實驗的緣由,因此通讀了一遍《Understanding git conceptually》,以爲確實不錯,因而就簡單地記錄一下。有的地方理解的還不是很深,可能不夠準確,等抽時間好好讀一下《Pro Git》。git
做者開篇說到:僅僅記住在何時用什麼命令是不夠的,出問題只是遲早的事。只有理解了Git的工做原理,纔算真正學會Git。遺憾的是大部分網上的教程都只是教你在什麼時候使用哪一個命令,而後讓你去模仿。說得這麼好,那就看看做者這篇教程是否把Git的工做原理講清楚了。注意:如下1.2.1到1.2.3都是本地化操做,不要看到倉庫、分支、合併等詞語就覺得必定是遠程操做了,1.2.4纔會講到多人協同開發時的遠程操做。markdown
使用Git的目的固然就是管理項目文件的版本變化,在Git中保存全部管理信息的數據結構叫作倉庫(Repository)。能夠在一個文件夾中經過命令git init
建立倉庫。倉庫保存在項目文件目錄下的一個叫作.git的文件夾中,它由兩部分組成:數據結構
例如,下面就是提交三次後的對象圖:分佈式
----> time ----->
(A) <-- (B) <-- (C)
^
|
master
^
|
HEAD
咱們平常的工做流程通常以下,下面就從圖的角度說明一下Git在背後到底作了什麼:fetch
git log
查看歷史記錄:顯示從HEAD到初始提交的全部提交日誌。用log命令顯示出每一個提交對象的SHA-1簽名,咱們就能控制HEAD的移動。git status
查看修改文件列表:顯示當前項目狀態與HEAD發生變化的文件列表。git diff
比較修改內容:顯示當前項目狀態與HEAD發生變化的文件內容。git commit -am "message"
提交修改:建立提交對象,將HEAD做爲父提交對象。提交完成後,HEAD將指向剛建立的新提交對象。-a參數至關於git add
,自動將修改文件添加到提交列表裏。下面說說Git中的分支(Branch)。在Git中,Branch與Head幾乎是等同的。每一個Branch都由一個Head來表示。因此,咱們用Branch指分支的Head以及它的全部父對象構成的整個歷史,用Head指單獨一個提交對象,通常是分支中最近的提交。Git分支的最佳實踐是:用分支實現新特性,保證master(主幹)始終處於可發佈的狀態。Git用戶常常會說:」commits are cheap」,當每一個開發者都在本身的分支上提交時是不用擔憂任何東西的,由於你不會影響到其餘人!spa
繼續上面提交三次的那個例子,咱們首先用git branch [new-head-name] [reference-to]
命令新建一個Head。其中,HEAD^表示HEAD的父級,若是未提供提交對象的話,默認爲HEAD。若是隻是執行git branch
則會列出全部Head:操作系統
git branch fix-headers HEAD^
(A) -- (B) ------- (C)
| |
fix-headers master
|
HEAD
要開始在新分支上工做,就要將新建立的Head設置爲HEAD,能夠經過git checkout [head-name]
完成。注意:checkout不僅會修改HEAD的指向,它還會重寫文件夾中的全部文件來匹配新HEAD指向提交對象所表示的項目狀態。因此,checkout以前最好提交全部修改。切換完成後,再提交後對象圖就變成了下面的樣子:日誌
+-------------- (D)
/ |
(A) -- (B) -- (C) |
| |
master fix-headers
|
HEAD
如今繼續看經常使用命令,腦海裏必定從圖的角度思考:code
git checkout [branch_name]
切換分支:切換HEAD指向的位置git checkout -t [branch_name]
新建分支當你在分支上完成開發時就須要將改動Merge回master,命令就是git merge [head]
和git pull . [head]
。假設當前Head叫作HEAD,分支Head叫作fix-headers,則Git的代碼合併過程以下:對象
fast-forward-merge的例子:
+-- (D) -- (E)
/ |
(A) -- (B) -- (C) |
| |
current fix-headers
|
HEAD
執行`git merge fix-headers`合併以後的樣子:
+-- (D) -- (E)
/ |
(A) -- (B) -- (C) |
|
fix-headers, current
|
HEAD
複雜狀況下的合併例子:
+---------- (D)
/ |
(A) -- (B) -- (C) -------------- (E)
| |
fix-headers master
|
HEAD
執行`git merge fix-headers`合併以後的樣子:
+---------- (D) ---------------+
/ | \
(A) -- (B) -- (C) -------------- (E) -- (F)
| |
fix-headers master
|
HEAD
前面講到過:Git的重要特色就是Repository與項目文件是保存在一塊兒的,因此Git能夠在無需連網的狀態下正常工做。可是這也意味着不一樣的開發者在默認狀況下是不共享Repository的。爲了實現共享,Git使用分佈式模型(Distribution Model)版本管理,既能夠無中心化也能夠有中心。
首先要訪問你朋友的遠程倉庫就須要一個位置,叫作remote-specification,Git能夠經過SSH、HTTP等協議對外提供訪問。而後就能夠經過git clone [remote-specification]
下載遠程倉庫到本地了。除了簡單的拷貝,Git會給遠程Repository建立一個reference叫作origin,同時還會給每一個Head新建一個Head,名字以」origin/」開頭來區分:
遠程倉庫的樣子:
+---------------(E)
/ |
(A) -- (B) -- (C) -- (D) |
| |
master feature
|
HEAD
你clone後的本地倉庫是這個樣子:
+-------------- (E)
/ |
(A) -- (B) -- (C) -- (D) |
| |
origin/master, master origin/feature
|
HEAD
當遠程倉庫變化時,咱們能夠經過git fetch [remote-repository-reference]
命令將改動抓取到本地,生成對應的提交對象,再用前面講過的git pull [remote-repository-reference] [remote-head-name]
進行合併。其實,pull命令也會自動進行fetch,因此平時咱們直接使用pull就能夠了。來看一個例子,假設你朋友在跟你一塊兒開發,他本地的Repository變成了這個樣子:
+-------- (E) -- (F) -- (G)
/ |
(A) -- (B) -- (C) -- (D) -- (H) |
| |
master feature
|
HEAD
在你執行fetch以後你的倉庫是這個樣子:
+------------ (E) ------------ (F) ---- (G)
/ | |
(A) -- (B) -- (C) -- (D) --------------- (H) |
| | | |
master feature origin/master origin/feature
|
HEAD
注意:你的Head並未受任何影響,變化的只是帶有"origin/"的Head。
如今就用pull命令合併,更新你的master和feature,完成後你的倉庫就成了這個樣子:
+-------- (E) ------------- (F) ----- (G)
/ | |
(A) -- (B) -- (C) -- (D) ------------ (H) |
| | |
feature origin/master, origin/feature
master
|
HEAD
與之相反,將本地修改發送到遠程倉庫使用git push [remote-repository-reference] [remote-head-name]
,遠程倉庫會負責提交對象的建立以及Head的合併移動等工做。要注意的是:向遠程倉庫push時,要求必須是fast-forward合併。
整理一下這部分的經常使用命令:
git pull [remote-repository-reference] [remote-head-name]
下載分支代碼