一、GIT發(fā)展史
同生活中的許多偉大事件一樣,Git 誕生于一個極富紛爭大舉創(chuàng)新的年代。Linux 內核開源項目有著為數眾廣的參與者。絕大多數的 Linux 內核維護工作都花在了提交補丁和保存歸檔的繁瑣事務上(1991-2002年間)。到 2002 年,整個項目組開始啟用分布式版本控制系統(tǒng) BitKeeper 來管理和維護代碼。
到 2005 年的時候,開發(fā) BitKeeper 的商業(yè)公司同 Linux 內核開源社區(qū)的合作關系結束,他們收回了免費使用 BitKeeper 的權力。這就迫使 Linux 開源社區(qū)(特別是 Linux 的締造者 Linus Torvalds )不得不吸取教訓,只有開發(fā)一套屬于自己的版本控制系統(tǒng)才不至于重蹈覆轍。他們對新的系統(tǒng)訂了若干目標:
- 速度
- 簡單的設計
- 對非線性開發(fā)模式的強力支持(允許上千個并行開發(fā)的分支)
- 完全分布式
- 有能力高效管理類似 Linux 內核一樣的超大規(guī)模項目(速度和數據量)
自誕生于 2005 年以來,Git 日臻成熟完善,在高度易用的同時,仍然保留著初期設定的目標。它的速度飛快,極其適合管理大項目,它還有著令人難以置信的非線性分支管理系統(tǒng),可以應付各種復雜的項目開發(fā)需求。
二、如何安裝GIT
1、Ubuntu上安裝
$ sudo apt-get install git-core
2、Windows上安裝
有個叫做 msysGit 的項目提供了安裝包,參考:
http://code.google.com/p/msysgit
3、從源碼安裝
Git的工作需要調用curl,zlib,openssl,expat,libiconv等庫的代碼,所有要先安裝這些依賴工具。
$ sudo apt-get install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
再從下面的 Git 官方站點下載最新版本源代碼 :
http://git-scm.com/download
然后編譯并安裝,例如:
$ tar -zxf git-1.6.0.5.tar.gz
$ cd git-1.6.0.5
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install
現在已經可以用 git 命令了,例如把 Git 項目倉庫克隆到本地,以便日后更新:
$ git clone git://git.kernel.org/pub/scm/git/git.git
三、運行GIT前的配置
一般在新的系統(tǒng)上,我們都需要先配置下自己的 Git 工作環(huán)境。配置工作只需一次,以后升級時還會沿用現在的配置。當然,如果需要,你隨時可以用相同的命令修改已有的配置。
Git 提供了一個叫做 git config 的工具,專門用來配置或讀取相應的工作環(huán)境變量。而正是由這些環(huán)境變量,決定了 Git 在各個環(huán)節(jié)的具體工作方式和行為。這些變量可以存放在以下三個不同的地方:
/etc/gitconfig文件:
系統(tǒng)中對所有用戶都普遍適用的配置。若使用 git config 時用 --system 選項,讀寫的就是這個文件。~/.gitconfig文件:用戶目錄下的配置文件只適用于該用戶。若使用 git config 時用 --global 選項,讀寫的就是這個文件。
當前項目的 git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這里的配置僅僅針對當前項目有效。每一個級別的配置都會覆蓋上層的相同配置,所以 .git/config 里的配置會覆蓋/etc/gitconfig 中的同名變量。
第一個要配置的是你個人的用戶名稱和電子郵件地址。這兩條配置很重要,每次 Git 提交時都會引用這兩條信息,說明是誰提交了更新,所以會隨更新內容一起被永久納入歷史記錄:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
如果用了 --global 選項,那么更改的配置文件就是位于你用戶主目錄下的那個,以后你所有的項目都會默認使用這里配置的用戶信息。如果要在某個特定的項目中使用其他名字或者電郵,只要去掉 --global 選項重新配置即可,新的設定保存在當前項目的 .git/config 文件里。
接下來要設置的是默認使用的文本編輯器。Git 需要你輸入一些額外消息的時候,會自動調用一個外部文本編輯器給你用。默認會使用操作系統(tǒng)指定的默認編輯器,一般可能會是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的話,可以重新設置:
$ git config --global core.editor emacs
還有一個比較常用的是,在解決合并沖突時使用哪種差異分析工具。比如要改用 vimdiff 的話:
$ git config --global merge.tool vimdiff
Git可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的輸出信息。當然,你也可以指定使用自己開發(fā)的工具。
查看配置信息
要檢查已有的配置信息,可以使用 git config --list 命令:
$ git config --list
user.name=Scott Chacon
user.email=schacon@gmail.com
也可以直接查閱某個環(huán)境變量的設定,只要把特定的名字跟在后面即可,例如:
git config user.name
Scott Chacon
四、獲取GIT幫助
想了解 Git 的各種命令該怎么用,可以閱讀它們的使用幫助,方法有三:
$ git help <verb>
$ git <verb> --help
$ man git-<verb>
比如,要學習 config 命令可以怎么用,運行:
$ git help config
我們隨時都可以瀏覽這些幫助信息而無需連網。不過,要是你覺得還不夠,可以到 Frenode IRC 服務器(irc.freenode.net)上的 #git 或 #github 頻道尋求他人幫助。這兩個頻道上總有著上百號人,大多都有著豐富的 git 知識,并且樂于助人。
五、Git中文件的狀態(tài)和基本工作流程
對于任何一個文件,在 Git 內都只有三種狀態(tài):已提交(committed),已修改(modified)和已暫存(staged)。已提交表示該文件已經被安全地保存在本地數據庫中了;已修改表示修改了某個文件,但還沒有提交保存;已暫存表示把已修改的文件放在下次提交時要保存的清單中。
由此我們看到 Git 管理項目時,文件流轉的三個工作區(qū)域:Git 的本地數據目錄,工作目錄以及暫存區(qū)域。

每個項目都有一個 git 目錄,它是 Git 用來保存元數據和對象數據庫的地方。該目錄非常重要,每次克隆鏡像倉庫的時候,實際拷貝的就是這個目錄里面的數據。
從項目中取出某個版本的所有文件和目錄,用以開始后續(xù)工作的叫做工作目錄。這些文件實際上都是從 git 目錄中的壓縮對象數據庫中提取出來的,接下來就可以在工作目錄中對這些文件進行編輯。
所謂的暫存區(qū)域只不過是個簡單的文件,一般都放在 git 目錄中。有時候人們會把這個文件叫做索引文件,不過標準說法還是叫暫存區(qū)域。
基本的 Git 工作流程如下所示:
- 在工作目錄中修改某些文件。
- 對這些修改了的文件作快照,并保存到暫存區(qū)域。
- 提交更新,將保存在暫存區(qū)域的文件快照轉儲到 git 目錄中。
所以,我們可以從文件所處的位置來判斷狀態(tài):如果是 git 目錄中保存著的特定版本文件,就屬于已提交狀態(tài);如果作了修改并已放入暫存區(qū)域,就屬于已暫存狀態(tài);如果自上次取出后,作了修改但還沒有放到暫存區(qū)域,就是已修改狀態(tài)。
六、Git常用命令
git init
git clone
git add <file>
git commit
git diff
git diff --cached
git rm <file>
git rm <file> --cached
git mv <file> <newfile>
git status
git log
git commit --amend
git reset HEAD <file>
git reset --soft HEAD~n
git reset --hard HEAD~n
git checkout -- <file>
git checkout <file>
git remote
git remote add [shortname] [url]
git fetch [remotename]
git push [remotename] [localbranch]:[remotebranch]
git remote show [remotename]
git remote rename [remotename] [new-remotename]
git remote rm [remotename]
git tag
git tag -a [tagname] –m [comments]
git push [remotename] [tagname]
git push [remotename] –tags
git branch [branchname]
git checkout [branchname]
git branch -d [branchname]
git merge [branchname]
git branch
git checkout -b [branchname] [remotename]/[branchname]
git push [remotename] :[remotebranch]
git rebase [branchname]
git pull
七、Git命令使用舉例
從當前目錄初始化
要對現有的某個項目開始用 Git 管理,只需到此項目所在的目錄,執(zhí)行:
git init
初始化后,在當前目錄下會出現一個名為 .git 的目錄,所有 Git 需要的數據和資源都存放在這個目錄中。從現有倉庫克隆,如克隆git的代碼庫
git clone git://git.kernel.org/pub/scm/git/git.git跟蹤新文件和暫存已修改文件
git add <file>提交更新
git commit
這種方式會啟動文本編輯器以便輸入本次提交的說明。也可以使用 -m 參數后跟提交說明的方式,在一行命令中提交更新:
git commit -m “Initial commit of test repo”查看已暫存和未暫存的修改
git diff
此命令比較的是工作目錄中當前文件和暫存區(qū)域快照之間的差異,也就是修改之后還沒有暫存起來的變化內容。若要看已經暫存起來的文件和上次提交時的快照之間的差異,可以用 git diff --cached 命令移除文件
要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區(qū)域移除),然后提交。
git rm <file>
另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區(qū)域移除),但仍然希望保留在當前工作目錄中。 可以用--cached選項
git rm --cached <file>移動文件
git mv <file> <newfile>檢查當前文件狀態(tài)
git status查看提交歷史
git log修改最后一次提交
有時候我們提交完了才發(fā)現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛才的提交操作,可以使用 --amend 選項重新提交:
git commit --amend
如果剛才提交完沒有作任何改動,直接運行此命令的話,相當于有機會重新編輯提交說明,而所提交的文件快照和之前的一樣。如果剛才提交時忘了暫存某些修改,可以先補上暫存操作,然后再運行 --amend 提交。取消已經暫存的文件
git reset HEAD <file>取消對文件的修改
git checkout -- <file>
git checkout <file>一般與上面的命令效果相同,但如果有一個分支名與文件名相同,就不一樣了。加--來消除歧義。查看當前的遠程庫
git remote
也可以加上 -v 選項,顯示對應的克隆地址
git remote -v添加遠程倉庫
git remote add [shortname] [url]從遠程倉庫抓取數據
git fetch [remotename]
此命令會到遠程倉庫中拉取所有你本地倉庫中還沒有的數據。運行完成后,你就可以在本地訪問該遠程倉庫中的所有分支,將其中某個分支合并到本地,或者只是取出某個分支,一探究竟。推送數據到遠程倉庫
git push [remotename] [localbranch]:[remotebranch]查看遠程倉庫信息
git remote show [remotename]遠程倉庫的刪除和重命名
git remote rename [remotename] [new-remotename]
git remote rm [remotename]
顯示已有的標簽
git tag新建標簽
git tag -a [tagname] –m [comments]
如果想為以前的某次提交打標簽,只要在打標簽的時候跟上對應提交對象的校驗和(或前幾位字符)即可 。用某個標簽新建分支
git checkout –b [branchname] [tagname]分享標簽
默認情況下,git push 并不會把標簽傳送到遠端服務器上,只有通過顯式命令才能分享標簽到遠端倉庫。
git push [remotename] [tagname]
如果要一次推送所有(本地新增的)標簽上去,可以使用 --tags 選項:
git push [remotename] –tags創(chuàng)建分支
git branch [branchname]切換分支
git checkout [branchname]新建并切換到該分支
git checkout -b [branchname]刪除分支
git branch -d [branchname]合并分支
git merge [branchname]
以上命令將[branchname]分支合并到當前分支查看分支
git branch遠程分支和創(chuàng)建跟蹤分支
遠程分支(remote branch)是對遠程倉庫狀態(tài)的索引。它們是一些無法移動的本地分支;只有在進行 Git 的網絡活動時才會更新。遠程分支就像是書簽,提醒著你上次連接遠程倉庫時上面各分支的位置。我們用 (遠程倉庫名)/(分支名) 這樣的形式表示遠程分支。從遠程分支檢出的本地分支成為跟蹤分支。
git checkout -b [branchname] [remotename]/[branchname]
或者git checkout --track [remotename]/[branchname]刪除遠程分支
git branch -r -d origin/[branchname]衍合
git rebase [branchname]從遠程倉庫抓取數據并merge
git pull
八、Git分支
在 Git 中提交時,會保存一個提交(commit)對象,它包含一個指向暫存內容快照的指針,作者和相關附屬信息,以及一定數量(也可能沒有)指向該提交對象直接祖先的指針:第一次提交是沒有直接祖先的,普通提交有一個祖先,由兩個或多個分支合并產生的提交則有多個祖先。
為直觀起見,我們假設在工作目錄中有三個文件,準備將它們暫存后提交。暫存操作會對每一個文件計算校驗和(即SHA-1 哈希字串),然后把當前版本的文件快照保存到 Git 倉庫中(Git 使用 blob 類型的對象存儲這些快照),并將校驗和加入暫存區(qū)域?,F在,Git 倉庫中有五個對象:三個表示文件快照內容的 blob 對象;一個記錄著目錄樹內容及其中各個文件對應 blob 對象索引的 tree 對象;以及一個包含指向 tree 對象(根目錄)的索引和其他提交信息元數據的 commit 對象 。
概念上來說,倉庫中的各個對象保存的數據和相互關系看起來 如下所示:

多次提交后,倉庫歷史如下:

Git 中的分支,其實本質上僅僅是個指向 commit 對象的可變指針。

Git 通過個git branch命令創(chuàng)建分支,比如新建一個testing分支:
git branch testing
這會在當前commit對象上新建一個分支指針, 同時Git保存著一個名為HEAD的特別指針,來讓Git知道當前在哪個分支上工作。如下:

要切換到其他分支,可以執(zhí)行命令git checkout,如切換到新建的testing分支:
git checkout testing
這樣HEAD就指向testing分支,如下:

此時在testing分支上工作,如果有新的提交,提交后的結果如下:

此時切換回master分支再提交后的結果如下:

九、Git分支合并與衍合
如將experiment分支合并回master分支執(zhí)行以下命令:
git checkout master
git merge experiment```
合并前后如下所示:

有時候合并操作并不會如此順利。如果你修改了兩個待合并分支里同一個文件的同一部分,Git 就無法干凈地把兩者合到一起,這種問題只能由人來解決。如果你想用一個有圖形界面的工具來解決這些問題,不妨運行 git mergetool,它會調用一個可視化的合并工具并引導你解決所有沖突。確認所有沖突都解決后,可以用 git commit 來完成這次合并提交。
其實,合并兩個分支還有另外一個選擇:你可以把在 C3 里產生的變化補丁重新在 C4 的基礎上打一遍。在 Git 里,這種操作叫做衍合(rebase)。有了 rebase 命令,就可以把在一個分支里提交的改變在另一個分支里重放一遍。
git checkout experiment
git rebase master
衍合前后如下所示:

再進行一次快進:
git checkout master
git merge experiment
結果如下所示:

現在,合并后的 C3(即現在的 C3’)所指的快照,同三方合并例子中的 C5 所指的快照內容一模一樣了。最后整合得到的結果沒有任何區(qū)別,但衍合能產生一個更為整潔的提交歷史。如果視察一個衍合過的分支的歷史記錄,看起來更清楚:仿佛所有修改都是先后進行的,盡管實際上它們原來是同時發(fā)生的。
但衍合也并不是完美無缺的,一句話可以總結這點:
永遠不要衍合那些已經推送到公共倉庫的更新。