开源分布式版本控制工具 —— Git 之旅
Git 主張的分布式代碼庫與文件快照的設計思想,相對于傳統 CVS、SVN 等集中式、文件差異式版本控制工具是一種挑戰與顛覆。Git 帶來了離線提交、輕量級分支等諸多便利。不過,也有人質疑 Git 的復雜性,并由此拔高了學習成本,某種程序上影響了開發者使用或者遷移 Git 的項目進度,筆者同樣感同身受,這也是本文的出發點。
不同于各種 Git 用法指南,本文在介紹 Git 安裝、使用的同時,更加注重于 Git 的設計思想、體系架構、以及各種實用功能,包括 Git 分支模型、Git 標簽、Git 補丁提交、CVS 遷移 Git、SVN 遷移 Git 等。
背景
Git 是一個開源的分布式版本控制軟件。在英式英語中,Git 指一個愚笨或者不開心的人,恐怕與 Git 發明人——Linux 教父 Linus Torvalds 當時的自嘲心理不無關系吧。2002 年之前,Linux 內核維護工作的絕大部分時間都浪費在提交補丁與保存歸檔等繁瑣事務上。啟用版本控制工具 BitKeeper 管理 Linux 內核成了當務之急。不過,BitKeeper 畢竟是一款商業軟件,在經歷了 3 年免費使用之后,Linux 社區不得不尋求它的替代品,以便繼續托管 Linux 內核源代碼。2005 年,迫于無奈,Linus Torvalds 自行開發了一套開源版本控制工具,并命名為 Git。
自誕生以來,Git 就以其開源、簡單、快捷、分布式、高效等特點,應付了類似 Linux 內核源代碼等各種復雜的項目開發需求。如今,Git 已經非常成熟,被廣泛接受與使用,越來越多的項目都遷移到 Git 倉庫中進行管理。以 Eclipse 社區為例。據稱,目前 80% 的 Eclipse 基金會項目已經完全使用 Git 管理,CVS 訪問權限已經切換成只讀狀態。并且,在 Eclipse 基金會官網中,針對項目管理的介紹中已將"CVS"三個字符劃掉,而且很萌地寫道,"Ding dong, the witch is dead.",意思是"叮咚,那個老巫婆已經掛了"。
不僅如此,筆者最近也收到了全球最大開源代碼托管平臺——SourceForge 的升級通知。其中,筆者的一個較為簡單的項目已經從 CVS 被系統默認自動升級到了 Git。而對于另一個較為復雜的 CVS 項目Toolbox for Java/JTOpen,SourceForge 并沒有自動升級,估計是等待筆者做升級前的最后準備工作。筆者希望通過分享自己的 Git 學習體驗與實踐經驗,對 Git 初學者有所裨益,這也是本文之意義所在。
回頁首
為什么選擇 Git
實際上,相對于 CVS、SVN 等主流版本控制軟件,Git 的學習成本甚至會更高。比如,對于 Subversion 用戶而言,如果能理解什么是文件、工作目錄、資源庫、版本、分支和標簽等概念,差不多就夠用了。而對于 Git 用戶,需要理解更多更復雜的概念,包括文件、快照、工作樹、索引、本地資源庫、遠程資源庫、遠程、提交、分支和 Stash 等。那么,為什么軟件開發者對 Git 還是趨之若鶩呢?相比于 CVS 與 SVN,Git 的優勢到底體現在哪里?
關于 Git 的各種優勢,互聯網以及各種 Git 書籍都給出了自己的答案。筆者認為,存儲快照與分布式的設計思想是 Git 的 2 大看點,理由如下:
第一,Git 底層自行維護的存儲文件系統是一大亮點。CVS、SVN 底層采用的為增量式文件系統,如圖 1 所示。增量式文件系統的特點是:當文件變動發生提交時,該文件系統存儲的是文件的差異信息。
圖 1. CVS、SVN 記錄文件內容差異
同樣是文件變更提交,Git 底層文件系統存儲的則為文件快照,即整個文件內容,并保存指向快照的索引,如圖 2 所示??紤]到性能因素,如果文件內容沒有發生任何變化,該文件系統則不會重復保存文件,只是簡單地保存文件的鏈接。
圖 2. Git 記錄整個文件快照
Git 之所以選擇這樣的底層存儲數據結構,主要是為了提高 Git 分支的使用效率。實際上,Git 分支本質上是一個指向索引對象的可變指針,而每一個索引對象又指向文件快照,如圖 3 所示。
圖 3. Git 分支對應的數據結構
這樣一來,創建分支可以瞬間完成,幾乎不需要花費太多代價。換句話說,Git 分支是廉價的、輕量級的。我們看看各種 CVS、SVN 項目,分支通常意味著源代碼的完整拷貝,其代價是昂貴的、重量級的。而對于大型項目來說,創建各種分支又是十分必要的,這與 Git 鼓勵頻繁創建與合并分支的理念相吻合。
第二,Git 版本控制系統的設計思想是"去中心化"。傳統的 CVS 、SVN 等工具采用的是 C/S 架構,只有一個中心代碼倉庫,位于服務器端。而一旦由于服務器系統宕機、網絡不通等各種原因造成中心倉庫不可用,整個 CVS 、SVN 系統的代碼檢入與檢出就癱瘓了。即便考慮到高可用性,通過遷移另一個中心倉庫繼續代碼提交操作,相應的運營維護成本也會隨之上升。
為了擺脫對中心倉庫的依賴,Git 的初始設計目標之一就是分布式控制管理。我們給出一個樣例,如圖 4 所示。假如我們成立一個項目組,開發者主要由 Alice、Bob、Clair、David 四名成員組成。其中,除了中心倉庫 origin(Git 默認遠程倉庫名稱)之外,每一名成員各自負責一個本地倉庫。從分布式的觀點來看,David 可看成是 Alice 的遠程倉庫,反過來也是一樣。Git 分布式的設計理念有助于減少對中心倉庫的依賴,從而有效降低中心倉庫的負載,改善代碼提交的靈活性。
圖 4. Git 分布式工作示意圖
Git 分布式設計思想所帶來的另外一大好處是支持離線工作。離線工作的好處不言而喻,對于 CVS、SVN 這種嚴重依賴網絡的 C/S 工具而言,沒有了網絡或者 VPN ,就意味著失去了左膀右臂,代碼檢入與檢出操作就無法正常進行。而一旦使用 Git ,即便在沒有 WIFI 的飛機或者火車上,照樣可以頻繁地提交代碼,只不過先提交到本地倉庫,等到了網絡連通的時候,再上傳到遠程的鏡像倉庫。
有關 Git 更多詳細信息,請參考 Git 官方網站:http://git-scm.com/?。
工欲善其事,必先利其器。在理解 Git 靈活的快照存儲與分布式設計理念之后,我們介紹 Git 針對不同操作系統的安裝過程。需要指出的是,這里僅僅粗線條地介紹 Git 的安裝方法,至于 Git 安裝前提條件、安裝過程出現的問題診斷等更加詳細的內容描述,均不在本文的討論范圍。
回頁首
如何安裝 Git
總結起來,Git 安裝方式通常分為兩種:一種是選擇 Git 源碼編譯安裝;另一種使用針對特定平臺的二進制安裝包,又可以細分為 Linux、Mac、Windows 等,其安裝說明如下。
1.源碼編譯安裝
從 Git 源碼安裝至少可以保證版本是最新的。在安裝 Git 之前,需要安裝其依賴的軟件包,包括 curl、zlib、openssl、expat、libiconv 等。根據不同類型的 Linux,讀者可以選擇不同的軟件包安裝工具,這里以 yum 為例,其安裝命令如下:
$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
接下來,讀者可以從 Git 官方站點 http://git-scm.com/download 下載最新 Git 源代碼(由于時間的差異,筆者無法保證本文所述 Git 為最新版本),執行以下命令編譯安裝。
$ tar -zxf git-1.7.6.tar.gz $ cd git-1.7.6 $ make prefix=/usr/local all $ sudo make prefix=/usr/local install
最后,敲入 git 命令,檢驗安裝是否成功,如圖 5 所示。可以看到,我們已經成功安裝 Git 了。
圖 5. 通過源碼安裝 Git
2.在 Linux 上安裝
要在 Linux 上安裝預編譯好的 Git 二進制安裝包,可選擇系統支持的軟件包管理器。對于紅帽 Linux,使用 yum 命令安裝:
$ yum install git-core
而對于 Ubuntu 這類 Debian 體系的 Linux 系統,使用 apt-get 命令安裝:
$ apt-get install git-core
由于此種安裝方式非常簡單,這里不做貼圖展示了。
3.在 Mac 上安裝
Mac 系統支持 Git 安裝的方式分為兩種:編譯安裝與圖形安裝。其命令行式的編譯安裝與 Linux 大同小異,這里不再介紹。相比之下,Mac 圖形安裝 Git 更加簡單,其安裝截圖如圖 6 所示。讀者可去 http://code.google.com/p/git-osx-installer 下載最新支持 Mac 系統的 Git 版本。
圖 6. 從 Mac 上安裝 Git
4.在 Windows 上安裝
與之前所述的 Mac 安裝 Git 一樣,在 Windows 上安裝 Git 也同樣輕松。根據用戶的使用習慣,我們又可以大致分為三類:
習慣命令行的用戶,可選擇 msysGit 安裝包,安裝截圖如 7 所示。msysGit 的官方下載地址為:http://code.google.com/p/msysgit。
圖 7. 從 Windows 上安裝命令行 Git 工具——msysGit
對于習慣 Tortoise 風格的用戶,可以選擇 TortoiseGit 安裝包,安裝后的右鍵截圖如圖 8 所示。TortoiseGit 的下載地址為:http://code.google.com/p/tortoisegit/。
圖 8. 從 Windows 上安裝“右鍵”Git 工具——TortoiseGit
而對于習慣 Eclipse 風格的用戶,可以選擇 Eclipse 插件——EGit 方式安裝,其 Git Repositories 視圖截圖如圖 9 所示。EGit 的下載地址為:http://download.eclipse.org/egit/updates。
圖 9. 從 Windows 上安裝 Eclipse 的 Git 插件——EGit
無論是哪一種安裝方式,如果是第一次使用 Git,均需要配置用戶信息,包括用戶名與 Email(如下所示),以便以后每次 Git 提交時都可以自動引用這兩條信息,說明是誰更新與提交了代碼。
$ git config --global user.name "Pi Guang Ming" $ git config --global user.email piguangming@gmail.com
到此為止,我們已經介紹了 Git 的分布式模型、快照模型、針對不同操作系統平臺的 Git 安裝之后,接下來是本文的主題內容,即 Git 的使用。
回頁首
如何使用 Git
前面提到,這一部分是本文的重點。我們將主要精力集中在 Git 底層的工作原理、以及實際工程中較為實用的 Git 分支、標簽、補丁、CVS 與 SVN 針對 Git 的遷移等,關于 Git 的各種基礎命令語法、解釋說明,以及本文沒有涉及到的內容,均可參見 Git 相關使用指南。
創建 Git 項目倉庫
在正式使用 Git 之前,我們至少需要創建一個 Git 代碼倉庫(簡稱 Git 倉庫)。通常而言,取得一個 Git 倉庫的方法有兩種。第一種是在現存的目錄下,通過導入所有文件來創建新的 Git 倉庫;第二種是從遠程 Git 鏡像倉庫直接克隆到本地倉庫。
針對第一類 Git 倉庫,我們可以使用 git init 命令創建一個嶄新的 Git 項目倉庫,如下:
$ git init
初始化 Git 后,在當前目錄下會出現一個名為 .git 的隱藏目錄,如圖 10 所示。
圖 10. .git 目錄
之所以特意強調 .git 目錄,是因為它十分重要。對于一個 Git 倉庫來說,其 .git 目錄保存了整個 Git 項目的所有數據與資源。關于 .git 目錄中各種文件的簡要解釋說明,如表 1 所示。如果需要了解詳細信息,請參見 Git 官方網站:http://git-scm.com/?。
表 1 .git 目錄簡要說明
| 子目錄名 | 簡要描述 |
|---|---|
| branches | Git 項目分支信息,新版 Git 已經不再使用該目錄。 |
| config | Git 項目配置信息 |
| description | Git 項目描述信息 |
| HEAD | 指向 Git 項目當前分支的頭指針 |
| hooks | 默認的"hooks"腳本,被特定事件發生前后觸發。 |
| info | 里面含一個 exclude 文件,指 Git 項目要忽略的文件。 |
| objects | Git 的數據對象,包括:commits, trees, blobs, tags。 |
| refs | 指向所有 Git 項目分支的指針 |
針對第二類 Git 倉庫,我們不需要 git init 初始化倉庫,取而代之的是,使用 git clone 直接將遠程鏡像克隆到本地倉庫。這里,我們以下載 Git 軟件本身的源代碼為例,其 git clone 命令如下:
git clone git://git.kernel.org/pub/scm/git/git.git
通過 ls git 命令,我們可以查看 Git 倉庫中的內容,如圖 11 所示。需要說明的是,針對遠程倉庫的鏡像,實際拷貝的就是 .git 目錄下的數據,然后根據元數據恢復成原來的整個項目結構,也即是圖 11 所示的內容。
圖 11. 克隆 Git 源代碼
此外,除了 git:// 協議,針對不同的使用場景,git clone 還支持 ssh://、http(s):// 等各種不同協議。
Git 對象模型
應該說,Git 對象模型是整個 Git 設計思想中最核心的部分。理解 Git 對象模型是理解整個 Git 的關鍵。簡單來說,每個 Git 對象包含三部分:類型,大小和內容。其中,對象的類型又分為 commits, trees, blobs, tags,其簡要說明如下:
blob 對象:一塊二進制數據,用來存儲文件數據,通常是一個文件。
tree 對象:指向 blob 對象或是其它 tree 對象的指針,一般用來表示內容之間的目錄層次關系。
commit 對象:一個 commit 對象只指向一個 tree 對象,用來標記項目某一個特定時間點的狀態,如時間戳、父對象、作者、提交者等。
tag 對象:與 CVS、SVN 標簽的概念類似。
接下來,我們結合一個示例來解釋不同 Git 對象之間的關系。圖 12 展示的是一個樣例 Ruby 項目,可以看出,這個例子非常簡單,僅作示意。
圖 12. Ruby 項目的目錄層次結構
如果我們把該項目提交到 Git 倉庫中,那么它的 Git 對象關系就如圖 13 所示。其中,3 個 blob 對象分別對應 README、mylib.rb、yourlib.rb 三個文件的內容快照。而 3 個 tree 對象指針則完整描述了項目的整個目錄結構,包括目錄樹內容、文件與 blob 對象的對應關系,各個文件對應 blob 對象索引等信息。而每一次提交都會生成一個 commit 對象指針,指向 tree 對象樹的根節點,不僅如此,commit 對象還包含作者、提交人等詳細信息。
圖 13. Ruby 項目的 Git 對象關系圖
不難看出,眾多 tree 對象與 blob 一起,作為內容節點(目錄或文件),構成了一個有向無環圖。在任何時候,通過與 commit 對象關聯的根節點,就可以遍歷出整個項目在本次提交時的所有內容。而前面提到,Git 分支本質上是指向 commit 對象的指針。兩個 Git 分支的合并,實質上是等價于兩個有向無環圖的合并,而有向無環圖可以讓 Git 更加高效判斷分支共同的父節點。因此,Git 對象模型設計賦予了開發人員最大的靈活性來任意創建分支,并在自己的分支上進行開發。
盡管以上幾種對象的類型不同,每一種對象都擁有同一長度的唯一標識,以 40 位字符串表示。實際上,圖 13 的對象標識均為簡寫,其中,commit 對象完整的標識如下 :
98ca9e0acb0be0321191a59e1d34ba5c867fa3
為保證對象標識的唯一性,Git 采用了 SHA1 哈希算法。這樣做,起碼有三大好處:
Git 只要比較對象名,就可以很快的判斷兩個對象是否相同。
由于每個倉庫中"對象名"的計算方法都完全一樣,因此,如果同樣的內容存在兩個不同的倉庫中,就會存在相同的"對象名"下。
Git 還可以通過檢查對象內容的 SHA1 哈希值與"對象名"是否相同,來判斷對象內容是否正確。
總結一下 Git 對象模型,blob 對象即項目中的所有實體文件,包括源代碼、圖片資源、xml 配置信息等內容。特別需要強調的是,blob 對象記錄的僅僅是文件內容,而關于文件所在目錄、名字大小等信息,則統統記錄在關聯它的 tree 對象上。我們每次提交文件,都會產生一個 commit 對象,并更新改動文件所關聯的 tree 對象。
Git 三種狀態
在理解 Git 對象模型之后,我們的焦點轉向 Git 文件的檢入與檢出。Git 倉庫模型大致分為三個工作區域,分別為工作目錄(Working Directory),暫存區域(Stage 或 Index),以及本地倉庫(History),相應的檢入與檢出命令如圖 14 所示:
圖 14. Git 三種狀態之間的轉換(1)
相關命令的簡要說明如下:
git add files:把當前工作文件拷貝到暫存區域。
git commit:在暫存區域生成文件快照并提交到本地倉庫。
git reset -- files:用來撤銷最后一次 git add files,也可以用 git reset 撤銷所有暫存區域文件。
git checkout -- files:把文件從暫存區域覆蓋到工作目錄,用來丟棄本地修改。
作為示例,圖 15 演示了如何通過 git add 與 git checkout 分別在工作目錄與暫存區之間來回復制,讀者可以自行嘗試 git commit 與 git reset 命令。首先,我們在工作目錄創建一個內容為"hello git"的 test 文件,通過 git add 命令將 test 文件復制到暫存區。然后,在工作目錄修改 test 文件,添加一行"hello git branch"。此時,暫存區的內容依然為"hello git",沒有改變。最后,通過 git checkout 將暫存區的 test 文件覆蓋工作目錄,即放棄了本地修改,最終文件內容為"hello git"。
圖 15. git checkout -- files 示例
實際上,工作目錄與倉庫之間的復制也可以一步到位,如圖 16 所示。
圖 16. Git 三種狀態之間的轉換(2)
其中,git commit -a等價于 git add 與 git commit,即先把文件從工作目錄復制到暫存區,然后再從暫存區復制到倉庫中。git checkout HEAD --files的過程剛好相反,即回滾到最后一次提交。
為了查看工作目錄,暫存區域,以及本地倉庫的文件有哪些不同,可以使用 git diff 命令,如圖 17 所示:
圖 17. Git 三種狀態之間的比較
git diff 命令相關的簡要說明如下:
git diff:查看尚未暫存的文件更新了哪些部分。
git diff --cached:查看已暫存文件和上次提交時的快照之間的差異。
git diff HEAD:查看未暫存文件與最新提交文件快照的區別。
git diff <index1> <index2>:查看不同快照之間的區別。
作為示例,圖 18 演示了 git diff 的用法??梢钥吹剑ㄟ^ git diff 比較,知道工作目錄比暫存區多了一行"hello git tag";而通過 git diff HEAD 比較,知道工作目錄又比倉庫最新提交文件多了兩行,分別是"hello git branch"與"hello git tag"。由此推斷,暫存區比倉庫多了一行"hello git branch",而這恰好與 git diff –cached 的結論相吻合。
圖 18. Git 三種狀態之間的比較——示例
以上是關于 Git 檢入與檢出操作的基礎用法,關于更詳細命令以及語法說明,可參見相關 Git 學習指南。
接下來,我們介紹 Git 更加高級的功能與特性。
Git 分支模型
前面提到,Git 中的分支本質上是一個指向 commit 對象的可變指針。Git 會維護一個默認分支——master。每一次提交之后,master 指針都會自動向前移動。而如果要創建一個新的分支,可以使用 git branch 命令:
$ git branch bugFix
這會在當前 commit 對象上新建一個分支指針,如圖 19 所示。
圖 19. 新建分支 bugFix
那么,Git 是如何知道當前在哪個分支上工作的呢?其實答案也很簡單,它保存著一個名為 HEAD 的特別指針,它是一個指向當前工作分支的指針。我們可以將 HEAD 想象為當前分支的別名。在這一點上,它和 CVS、SVN 的 HEAD 概念大不相同。
運行 git branch 命令,僅僅是建立了一個新的分支,但不會自動切換到這個分支中去,所以在這個例子中,我們依然還在 master 分支里工作。要切換到其他分支,可以執行 git checkout 命令。
$ git checkout bugFix
這樣 HEAD 就指向了 bugFix 分支,見圖 20 所示。
圖 20. 切換到 bugFix 分支
實際上,我們可以將分支的創建與切換兩步合二為一。要新建并切換到該分支,運行 git checkout 并加上 -b 參數:
$ git checkout -b bugFix
接下來,再提交一次:
$ vi test.rb $ git commit -a -m 'update copyright'
圖 21 展示了提交后的結果。非常有趣,現在 bugFix 分支向前移動了一格,而 master 分支仍然指向原先 git checkout 時所在的 commit 對象。
圖 21. 在 bugFix 分支提交文件
我們再切換到 master 分支:
$ git checkout master
其結構如圖 22 所示。這條命令做了兩件事。第一,它把 HEAD 指針移回到 master 分支;第二,把工作目錄中的文件替換成了 master 分支所指向的快照內容。也就是說,從現在開始,基于該文件的一系列提交都將始于一個較老的版本。它的主要作用在于,可以將 bugFix 分支里作出的修改暫時取消,隔離 bugFix 分支對 master 分支的影響。在實際項目中,我們經常有這樣的需求,即采用 developer 分支開發主要版本,bugFix 分支負責修復 bug,彼此互相隔離,最后合并。
圖 22. 切換成 master 分支
我們作些修改后再次提交:
$ vi test.rb $ git commit -a -m 'made other changes'
現在我們的項目提交歷史產生了分叉,如圖 23 所示,原因是剛才我們創建了一個分支,進行了一些工作,然后又切換到主分支進行了另一些工作。我們可以在不同分支里反復切換,并在時機成熟時將它們合并到一起。
圖 23. 在 master 分支提交文件
git merge 命令把不同分支合并起來。合并前,HEAD 必須指向當前最新的提交。按使用場景不同,git merge 操作又分為三種情況:
如果另一個分支是當前提交的祖父節點,那么 git merge 命令將什么也不做。
反過來,如果當前提交是另一個分支的祖父節點,就導致 fast-forward 合并。指向只是簡單的移動,并生成一個新的提交。
否則就是一次真正的合并。默認把當前提交 (ed489 如下所示 ) 和另一個提交 (33104) 以及他們的共同祖父節點 (b325c) 進行一次三方合并。結果是先保存當前目錄和索引,然后和父節點 33104 一起做一次新提交,如圖 24 所示。
圖 24. 合并分支
可以看到,git merge 命令把兩個父分支合并進行一次提交,但提交歷史不是線性的。相比之下,分支衍合命令 git rebase 在當前分支上重演另一個分支的歷史,從而保證提交歷史是線性的,如圖 25 所示。
圖 25. 衍合分支
作為示例,我們演示關于 git merge 與 git rebase 的區別,見圖 26 所示。
圖 26. 合并分支 vs 衍合分支
有時候合并操作并不會如此順利,如果在不同的分支中都修改了同一個文件的同一部分,會造成合并沖突,Git 就無法干凈地把兩者合到一起。此時,Git 僅作合并,但不提交,它會停下來等人為地解決沖突,如下:
$ cat test.rb init master update1 master update2 bugFix update1 <<<<<<< HEAD master updated3 ======= bugFix update2 >>>>>>> bugFix
要查看哪些文件在合并時發生沖突,可以使用 git status :
$ git status # On branch master # Unmerged paths: # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: test.rb # no changes added to commit (use "git add" and/or "git commit -a")
待修補發布以后,bugFix 分支已經完成了歷史使命,我們可以使用 git branch 的 -d 選項執行刪除操作:
$ git branch -d bugFix
以上,我們介紹了 Git 分支的創建,切換,合并(線性與非線性),沖突,以及刪除。
Git 標簽
與 CVS、SVN 等其它版本控制系統一樣,Git 也支持打 Git 標簽。在程序開發到一個階段后,我們需要打個標簽,發布一個版本,如0.1.2,v0.1.2 等。
Git 使用的標簽有兩種類型:輕量級的(lightweight)和含附注的(annotated)。輕量級標簽實際上就是個指向特定提交對象的引用;而含附注標簽實際上是存儲在倉庫中的一個獨立 Git 對象。相比之下,含附注標簽包含信息更多,包括自身校驗信息,標簽名字,Email,標簽日期,以及標簽說明等。含附注標簽本身也允許使用 GNU Privacy Guard (GPG) 來簽署或驗證,因此我們推薦使用含附注的標簽,以便保留相關信息。
要打上標簽,可執行以下 Git 命令:
$ git tag -a v0.1.2 -m "Release version 0.1.2"
相應地,要查看標簽,執行下列 Git 命令:
$ git tag –l
當然,也可采用 git show 命令查看標簽版本與提交對象等詳細信息。
$ git show v0.1.2
刪除標簽的 Git 命令如下:
git tag -d v0.1.2
如果我們有自己的私鑰,還可以用 GPG 來簽署標簽,只需要把之前的 -a 改為 -s,如下
$ git tag -s v0.1.2 -m "My signed 0.1.2 tag"
要驗證已經簽署的標簽,可以先取到對應的公鑰,然后使用 git tag –v 命令驗證,如下:
$ git tag -v v0.1.2
需要注意的,默認情況下,git push 并不會把標簽傳送到遠端倉庫上。我們只能通過顯式命令才能分享標簽。其命令格式如下:
$ git push origin v0.1.2
如果希望一次性推送所有本地新增的標簽,可以使用 --tags 選項:
$ git push origin --tags
如此一來,其他人克隆共享倉庫或拉取數據同步后,也會看到這些標簽。
Git 補丁
UNIX 世界中,補丁(Patch)的概念非常重要,幾乎所有大型 UNIX 項目的普通貢獻者,都是通過補丁來提交代碼。對于 Linux 內核項目而言,普通開發者先從 Git 項目倉庫克隆下代碼,然后寫入代碼,做一個補丁,最后用 E-mail 發給 Linux 內核的維護者就可以了。
Git 提供了兩種簡單的補丁生成方案。一是使用 git diff 生成的標準補丁,二是使用 git format-patch 生成的 Git 專用補丁。這里,我們重點介紹第二種方式,關于第一種 git diff 方式,比較簡單,這里不做介紹。
假設我們有一個項目 myproj,其工作目錄里最初有一個文件 test,內容是"hello git",默認提交給 master 分支。這里,我們創建一個新分支 bugFix 用于代碼修改,如圖 27 所示:
圖 27. 創建分支
接下來,我們在 test 文件里面追加一行"fix",并使用 git format-patch 生成一個 patch,如圖 28 所示,其中,git format-patch 的 -M 選項表示這個 patch 要和哪個分支比對。
圖 28. 生成補丁
可以看到,補丁文件 0001-fix.patch 包含各種信息,不僅有 diff 的信息,還有提交者,時間等等。仔細一看你會發現,這是個 E-mail 的文件,可以直接發送。
接下來,可以使用 git am 來應用補丁,如圖 29 所示??梢钥吹剑啾扔谠瓉淼?test 文件,打上補丁后,多了一行"fix"。
圖 29. 應用補丁
關于以上兩種生成補丁方式的比較,很明顯,相比于 git diff 生成的通用補丁,git format-patch 生成的 Git 專用補丁兼容性較弱。不過,Git 專用補丁中含有補丁開發者的名字,在應用補丁時,這個名字會被記錄進版本庫。因此,目前使用 Git 的開源社區往往建議大家使用 format-patch 生成補丁。
Git 遠程倉庫操作
前面提到,Git 是分布式版本控制系統。對于一個分布式節點來說,其它節點的 Git 倉庫都可以作為本地倉庫的遠程倉庫。要查看當前配置有哪些遠程倉庫,可以使用以下命令:
$ git remote
在克隆完某個項目后,至少可以看到一個名為 origin 的遠程庫,Git 默認使用這個名字來標識你所克隆的原始倉庫。
項目進行到一個階段,要同別人分享目前的成果,可以使用 git push 命令將本地倉庫中的數據推送到遠程倉庫。
$ git push origin master
而要將遠程倉庫抓取數據到本地,可以使用 git fetch 命令,從而獲取所有本地倉庫中還沒有的數據。
$ git fetch [remote-name]
如果設置了某個分支用于跟蹤某個遠端倉庫的分支,可以使用 git pull 命令自動抓取數據下來,然后將遠端分支自動合并到本地倉庫中當前分支。從這個角度,git pull 等價于 git fetch + git merge 的功能。
$ git pull [remote-name]
關于以上幾種 Git 遠程倉庫的相關操作,其關系見圖 30 所示。要了解 Git 遠程倉庫的更多命令,如刪除與重命名等,可參閱相關 Git 操作指南。
圖 30. Git 遠程倉庫的操作
CVS 遷移到 Git
對于想要從 CVS 遷移到 Git 的用戶,可以使用 git cvsimport 工具解決遷移問題,前提是安裝相關工具 git-cvs 或 cvsps。
關于 git-cvs 工具,可以使用 yum 或者 apt-get 命令安裝。以 yum 為例,其安裝命令如下:
$ yum install git-cvs
如果是源碼編譯安裝 Git,則需要安裝 cvsps,下載地址:http://www.cobite.com/cvsps/
$ tar -zxvf cvsps-2.1.tar.gz $ cd cvsps-2.1 $ make && make install
作為示例,我們新建一個目錄 jt400.cvs,并將文章開頭提到的 SourceForge 托管的 CVS 項目 Toolbox for Java/JTOpen 的源碼導入到 Git 中來,操作過程如下:
$ mkdir jt400.cvs $ cd jt400.cvs $ export CVSROOT=:pserver:piguangming@jt400.cvs.sourceforge.net:/cvsroot/jt400 $ cvs login $ git cvsimport -C src src 其中,-C src 是要往 git 倉庫里創建的項目名稱,最后那個 src 是 cvs 中要導入的模塊。
SVN 遷移到 Git
同樣,Git 也提供了 git svn 相關工具,提供 SVN 項目到 Git 的遷移,前提是安裝相關工具 subversion-perl。
$ yum install install subversion-perl 作為示例,我們新建一個目錄 photon-android.svn,并將 googlecode 托管的 SVN 項目 photon-android 導入到 Git 中來,操作過程如下: $ mkdir photon-android.svn $ cd photon-android.svn $ git svn clone http://photon-android.googlecode.com/svn/
回頁首
總結
本文系統性地介紹了分布式版本控制工具——Git,包括為什么使用 Git,Git 的安裝,Git 的工作原理,Git 的使用方法,CVS 與 SVN 向 Git 遷移等。有關 Git 更全面的使用方法,請參見文檔:https://github.com/progit/progit。
總結
以上是生活随笔為你收集整理的开源分布式版本控制工具 —— Git 之旅的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的std函数_Python
- 下一篇: max导出fbx设置_真3D虚拟偶像制作