1. 程式人生 > >Git命令家底兒及Git資料通訊原理詳解

Git命令家底兒及Git資料通訊原理詳解

Git是一款開源的分散式版本控制系統(VCS),常用的VCS工具還包括SVN、Mercurial等,他們的使命是對資源變化的進行版本管理控制,對資源容災備份,支援多域協同開發。這裡的資源不僅僅是系統程式碼,還包括圖片、檔案、網頁等。本篇文章結合流程圖、詳細的註解、例項操作針對Git的使用、Git資料通訊原理進行細緻的講解,利用半場足球賽的時間通讀全文後相信你面對Git會自信滿滿、知其所以然,使用起來遊刃有餘,當然對其他工具的理解也就非常容易了。

Git在各個作業系統的安裝過程就不綴文了,步驟都是固定的,按照步驟一步一步安裝就可以了。在開始講解之前,我們先對Git進行資源版本管理有個整體的瞭解,如圖1為Git資源狀態流轉過程,理解清楚這個流轉圖對Git命令的操作非常關鍵。

圖片描述

接下來,我們開始夢幻的Git命令之旅,瞭解下在專案協同工作過程中經常使用的Git命令。以下是我在工作過程中的總結(可以說是我的Git家底兒了),為了更清晰的展示,將命令操作分成了有序的多塊,大家在閱讀過程中可能會感覺到一絲筆記的色彩,沒錯,這就是這篇文章的特點,我相信這種方式會更利於大家理解。

(PS:下文中【】的內容為對命令的註解)
git init                       【初始化本地倉庫 ,將當前專案目錄加入git管理】
git add <filename||path>         【將新檔案加入版本控制,Git會對目標檔案進行跟蹤,納入版本控制管理。(這是個多功能命令,根據目標檔案的狀態不同,此命令的效果也不同:可以用它開始跟蹤新檔案,或者把已跟蹤的檔案放到暫存區,還能用於合併時把有衝突的檔案標記為已解決狀態等)】
git add
. 【將當前目錄所有檔案加入到版本控制】 git commit -m 'commit comment' 【提交變動,將修改的檔案轉移到暫存區】 git commit -a 'commit comment' 【將add和commit操作合併】 git commit --amend 【重新commit,將之前commit合併為一個(add上次commit漏掉的檔案或者重寫comment)】 Example: git commit -m 'commit comment' git add
filename git commit –amend

此例子只會產生一次commit log,第二個commit會覆蓋第一個commit。

圖片描述

取消已暫存到暫存域的檔案或者修改未提交的檔案,可以通過git status命令檢視取消到命令方案。

<–clone start–>

如果我們想加入一個現有專案的協同開發,可以通過clone命令將遠端reposity的專案克隆映象到本地,此映象(此處可以聯想下作業系統的映象)包含專案所有歷史變更,所有歷史版本,所有分支資訊等等,是遠端reposity的一個完整副本。Git支援多種資料傳輸協議,本地傳輸、git協議、ssh協議、http協議。

git clone source. zhouliwei.com/app/zhouliwei.git            【本地倉庫clone】 
git clone git+ssh://[email protected]/zhouliwei.git         【遠端倉庫clone(ssh協議)】 
git clone http://[email protected]/app/ zhouliwei.git     【遠端倉庫clone(http協議)】 
git clone   -l     /home/zhouliwei/test   【拷貝本地資源庫到當前目錄】 
 git clone   -b 分支名   http://[email protected] zhouliwei.com/app/test.git   【clone指定分支(類似checkout)】 
 git clone   -s   遠端地址  【作為共享倉庫】 

<–clone end–>

 git status           【檢視當前版本狀態。
該命令有幾個資訊塊:
on branch branchname:本地資源庫在branchname分支
changes not staged for commit:本地資源庫做了哪些修改,還未commit到暫存域 
new file:還沒有加入版本控制的新檔案
modified:有改動的檔案
deleted:被執行刪除的檔案 git rm filename
unmerged:出現衝突的檔案】

在協同開發的過程中,可能會對資源進行多次修改,多次提交,在一些場景下對提交歷史的回顧極為重要,我們可以藉助log命令完成此工作。

git log                【顯示所有歷史提交日誌,最近的在第一行】 
git log    -1           【顯示最近一行】 
git log    --stat       【顯示提交日誌及相關變動檔案,增改行統計】
git log  -p  -1   【詳細顯示每次提交的內容差異】      
git log  -p -m   

圖片描述

圖片描述

圖片描述

git clean --df             【是從工作目錄中移除沒有track的檔案】 
git rm –cached  <filename||path> 【將檔案或者路徑從遠端reposity、本地暫存域中刪除,在本地工作空間中保留,主要針對和專案本身無關的不小心提交到伺服器的檔案】
vim filename     【檢視、編輯資原始檔】

接下來我們瞭解下Git branch,分支可以說是一個非常具有魅力的創造,他將協作的成員工作獨立起來,互不影響,各自沿著自己的主線向前推進,他們以master分支作為共同的資源集散地,所有分支生成於master,最終又迴歸到master。圖2為Git 的分支模型。

圖片描述

<–branch start–>

每次commit都會在暫存域中生成一個快照物件,生成一個新的版本,分支就是指向快照物件的可變指標。可以通過HEAD定位到當前在哪個分支工作,HEAD是一個指向正在工作本地分支的特殊指標,可以通過checkout將HEAD切換成目標分支。HEAD會隨著當前分支的commit而移動,其他分支不受影響。

  git branch          【列出本地所有分支(已檢出)】 
  git branch -a       【列出本地+遠端所有分支】 
    git branch  -v      【可以看見每一個分支的最後一次提交】 
    git branch  -av 
    git branch  -r       【列出所有原創分支(origin/.)】 
    git branch  branchname    【建立一個新分支】 
    git branch   -d 分支名 【刪除一個分支】 
    git branch   -m oldbranch newbranch  【本地分支改名】 
    git branch   --contains 字串  【顯示包含目標字串的分支】 
    git branch   --merged            【顯示所有已合併到當前分支的分支】
    git branch   --no-merged         【顯示所有未合併到當前分支的分支】 
    git branch   --set-upstream  分支名 origin/分支名   【本地分支關聯到遠端路徑】 

<–branch end–>

<–checkout start–>

從遠端reposity checkout的下來的本地分支稱為跟蹤分支,跟蹤分支是一個和某個遠端分支對映的本地分支。clone之後本地會自動建立一個跟蹤分支master,對映到遠端的分支origin/master。

git checkout  branchname        【切換到新分支】 
git checkout   -b  branchname    【建立並切換到新的分支,如果本地已經有此分支則使用上個命令】 
git checkout  -b  branchname origin/branchname 【在本地建立新分支,從遠端拉取新分支程式碼】   
git checkout  filename          【替換本地改動,會從伺服器下載最新的檔案(HEAD 中最新的內容)覆蓋工作目錄中的檔案(add、commit的檔案不受影響),次這個操作是不可逆】 

<–checkout end–>

<–merge start–>

在協同專案工作的過程中,如果多個人同時修改一個檔案的相同地方,leader在master上進行合併時難免會出現程式碼衝突的情況,此時的merge會合並失敗,需要將衝突進行處理,我們可以採取下面方式進行處理。

git merge branchname || origin/branchname   【合併目標分支到當前分支,合併之後會生成一個新的快照物件】 

如果出現衝突,通過git status檢視衝突位置(標記為unmerged為重讀檔案)。我們可以通過手動修改成想要的程式碼, 解決衝突的時候可以用到git diff ,處理完之後用git add

git reset --hard HEAD  【將當前版本重置為HEAD(通常用於merge失敗回退)】 

丟棄所有的本地改動與提交:

git fetch origin  【1.從伺服器拉取最新版本】 
git reset --hard origin/master 【2.將你本地主分支指向到遠端分支】

<–merge end–>

<–fetch start–>

git fetch  --all 【 從遠處資源庫拉取所有分支(merge之後才會更新本地分支),可以進行diff、log 
git fetch  origin  【將從遠端拉取上次克隆後的master分支所有變化,即獲取master分支最新程式碼】

通過fetch命令合併程式碼過程:

git fetch     origin  branchname1   【1.  <遠端主機名> <分支名> 設定當前的fetch_head為分支branchname(fetch_head為每個分支在伺服器上的最新狀態)】 
git fetch     origin branchname1: branchname2   【2. 拉取遠端branchname1到本地新分支branchname2(branchname2是一個臨時分支) 】 
git fetch   diff    branchname2           【3. 將當前分支和新建的臨時分支branchname2進行比較】 
git fetch    merge branchname2       【 4. 將當前分支和新建的臨時分支branchname2進行合併,此時branchname1為最新程式碼】  
 git fetch   -d branchname2             【5.刪除臨時分支branchname2】 

git pull == git fetch + merge                  【從遠端拉取最新版本,合併】   
git pull origin  branchname1               【拉取併合並branchname1】          

使用git fetch操作性更好些(和pull對比),我們可以進行diff、log,再merge,更利於開發者根據當前情況進行鍼對性操作。

<–fetch end–>

<–push start–>

通過push命令將自己的分支資源和協同小組的其他人員進行共享,前提條件是Git賬戶必須擁有遠端reposity的寫許可權。

git pull  <遠端主機名> <遠端分支名>:<本地分支名> 
git push <遠端主機名> <本地分支名>:<遠端分支名>  【將本地分支推送到遠端分支】 
1) git push origin  <本地分支名>     【遠端分支名為空,將本地分支推送到遠端與其有對映關係的分支】 
2) git push origin  :<遠端分支名>   【本地分支名為空,將本地空分支推送到遠端分支,即刪除遠端分支】 
3) git push origin                           【將本地當前分支推送到遠端與其有對映關係的分支】 

<–push end–>

<–remote start–>

參與專案的協作開發,本地資源來源於遠端倉庫,所以需要對遠端倉庫的管理,比如遠端倉庫的建立、檢視、刪除、client-server資源對映等等。

git remote      【列出遠端所有alias別名,自己許可權範圍內的遠端reposity】 
git remote  -v  【可以看見每一個別名對應的實際url】 
git remote  add [alias] [url] 【給遠端url新增別名||把url新增為遠端倉庫】 
git remote add myRepo  /home/zhouliwei/test.git 【新增本地倉庫作為遠端倉庫,共享目錄】
git remote   rm   [alias]      【刪除一個別名】 
git remote   rename [old-alias] [new-alias]   【重新命名】
git remote   set-url [alias] [url]    【更新url. 可以加上—push和fetch引數,為同一個別名set不同的存取地址.  】
 git remote   add origin <server> 【將本地倉庫連線到遠端倉庫   git remote add origin http://[email protected]/app/test.git 然後可以通過git push origin branchname將branchname推送到相應遠端分支;建立遠端倉庫】
 git remote    show origin    【顯示遠端資訊】

<–remote end–>

將本地工作空間上傳到遠端新建倉庫操作:

首先在本地空間生成用於ssh加密傳輸的公鑰和私鑰,將公鑰維護到遠端倉庫的SSH key(後面會詳細介紹如何操作)。

git init
git add .
git commit –m ‘initial commit’
git remote  add origin http://[email protected].zhouliwei.com/app/test.git
git push orgin master

<–rebase start–>

可以通過rebase命令(衍合)以補丁的方式將某個分支的改動在其他分支上再打一遍(合併到其他分支),可以簡化分支的歷史操作記錄,流程看起來更清晰(和merge對比)。

git checkout branchname 
git rebase master

將branchname分支程式碼的改動衍合到master,相當於是在master上覆制了branchname分支的改動,只在master分支上生成操作歷史。

<–rebase end–>

至此我們已經完成了常用Git命令的講解,包括本地倉庫的建立初始化、克隆遠端資源,本地倉庫資源的修改、提交、推送,分支的管理,遠端倉庫資源的檢出,衝突處理,遠端倉庫管理、協同開發等等。每個命令模組講解過程中的註解已經非常詳細了,並且為了便於更好的理解列舉了相應的示例,對於使用Git進行專案協同開發的人員來說,上面的內容可以說是綽綽有餘了。

接下來,我們順著Git的夢幻之旅繼續往下走。專案協作的各個成員是如何與Git服務端進行資料通訊的呢?之前我們有提到Git支援四種資料傳輸協議,那麼我們來深入瞭解下這四種傳輸協議各自的優勢和不足。

傳輸協議 優勢 缺點
本地傳輸 1. 遠端倉庫部署在本地目錄,Git client-server之間的資料通訊類似本地檔案的複製剪下,資料的通訊速度較快;2. 資源的許可權沿用本地作業系統的檔案許可權和網路訪問許可權,不需要單獨配置。 1. 由於遠端倉庫在本地目錄,資源毀滅性丟失的危險性增大。
ssh協議(安全外殼傳輸協議ssh://) 1. 服務搭建相對較簡單;2. 基於公鑰私鑰對的方式進行加密授權資料傳輸;3. 同時支援資料的讀和寫操作。 1. 不支援匿名訪問,必須通過ssh訪問主機才能讀寫倉庫。
Git協議(git://) 1. 自身攜帶的傳輸協議,傳輸速度最快的協議;2. 使用類似ssh相同的資料傳輸機制,但取消了加密解密的開銷。 沒有授權機制,要麼所有客戶端都可讀,要麼所有客戶端都可寫,不能根據情況選擇性配置讀寫許可權;2. 服務搭建相對較複雜。
http/https協議(超文字傳輸協議) 服務搭建相對較簡單,基於Apache等web容器就可以實現;2. 授權機制簡單,能夠訪問Git倉庫所在伺服器的web服務的人都可以獲取遠端倉庫資源。 1. 資料通訊網路開銷較大;2. 執行寫操作需要基於ssh協議。

通過上面的對比分析,我們發現http/https是最簡單最流行的一種協議方式,ssh是最安全的一種協議方式,特別是在網際網路領域這一點尤為重要,並且http/https的寫操作也是基於ssh協議完成的,那麼我們繼續深入的瞭解下ssh通訊協議。

ssh資料通訊協議也稱作安全外殼協議,從他的名字就可以看出他使命就是確保安全資料傳輸,並且傳輸的資料會進行壓縮,降低網路傳輸消耗,提高資料傳輸速度。ssh協議是基於公鑰私鑰對的方式進行加密授權資料傳輸的,下面我們通過兩個加密演算法來理解公鑰私鑰——對稱加密演算法和非對稱加密演算法。

圖3為對稱加密演算法流程圖,資料的傳送方sender和接收方receiver通過相同的金鑰key對資料進行加密解密操作。祕鑰用於確保資料在公共通道傳輸過程中的安全性,即使密文資料在傳輸過程中被外部竊取,如果沒有金鑰也不能獲取其中的內容。

圖片描述

圖4為非對稱加密演算法流程圖,資料的傳送方sender和接收方receiver通過不同的金鑰key對資料進行加密解密操作。sender通過公鑰key1對明文資料進行加密,receiver通過私鑰key2對密文資料進行解密。公鑰和私鑰一定是成對出現的,如果一個檔案用公鑰進行加密,則可以通過私鑰進行解密;如果一個檔案用私鑰進行加密,則可以通過公鑰進行解密。比如,我們在網際網路環境中和其他合作方進行資料通訊,我們的私鑰是保密的,只有自己知道,公鑰可以分發給合作方1,合作方2等等,這些合作方可以通過公鑰對資料進行加密傳送給我們,然後我們通過自己的私鑰進行解密,並且在這個過程中合作方之間是獨立的資料安全的,不會看到其他其他合作方的資料,這也是非對稱加密的一個優勢。

圖片描述

接著,我們來看下如果生成屬於自己的公鑰和私鑰。

在Git視窗輸入ssh-keygen –t rsa –C “[email protected]”命令,回車,如圖5。

圖片描述

圖5 生成公鑰私鑰

在提示資訊的目錄中,我們看到生成兩個檔案,id_rsa.pub為公鑰檔案,id_rsa為私鑰檔案,如圖6。

(在Mac中,切換到.ssh目錄(cd .ssh),執行ssh-keygen –t rsa –C “[email protected]”,生成私鑰和公鑰)

圖片描述

圖6 公鑰私鑰檔案

生成的公鑰和私鑰怎麼使用呢?現在我們使用SourceTree基於ssh協議從github上clone一個專案,會發現圖7 ssh認證失敗提示。

圖片描述

圖7 ssh認證失敗

為什麼會ssh認證失敗呢?提示資訊描述的很清楚,我們需要將自己生成的公鑰加入到github維護的該專案中(這個操作由該專案管理員完成,一個專案可能會被加入多個公鑰),加入之後配合本地的私鑰就可以進行安全的資料通訊了,此時客戶端就擁有了該專案的寫許可權,然後重新嘗試clone,克隆專案成功。

回過頭來,思考下Git基於http/https通訊協議的寫許可權是不是也是通過這種方式實現的呢?答案是肯定的。

現在到了可以慶祝的時刻了,你不但可以熟練使用Git命令進行協同工作,還透徹的瞭解了Git資料通訊的內部原理。知其然並知其所以然,將知識運用到實踐中,才是研究技術的最高境界。(送人玫瑰,手留餘香)