若是你是一名謹慎的項目開發者,就一定會用到版本管理軟件,現有市場上成熟的版本管理軟件有cvs,svn,clearcase和更加靈活敏捷的git。相比傳統的重量級中央版本管理軟件svn,clearcase等,git更像是一個輕盈的分佈式版本管理系統,它不須要架設中央服務器來時時同步版本信息,我的只須要在本身的電腦上建立一個git版本管理庫,就擁有一個完整項目開發歷史的分支集合,最大程度支持自由軟件開發者的離線工做,同時git又提供了方便的同步接口協同其餘人的並行開發。
在實際的項目開發過程當中,咱們採用了雙系統來進行版本管理,svn管理項目正式版本和預發佈版本,而git管理子功能模塊開發和我的在服務器和客戶端之間的協同開發,在保證功能穩定以後再將git上的內容merge到svn主幹上。
這篇文章將主要結合真實的使用環境介紹git的各類基本操做,多人如何協同開發客戶端和服務端大功能模塊,避免頻繁提交svn來進行版本管理。html
git安裝
debian系統能夠用apt-get install git直接安裝
windows系統中能夠到http://code.google.com/p/msysgit/下載git for windows的版本,經過git的命令行進行操做,不建議使用tortoisegit等圖形化工具。
git 配置
使用git的第一件事就是設置你的名字和email,這些就是你在提交commit時的簽名。
$ git config --global user.name "usrname"
$ git config --global user.email " usrname@gmail.com"
執行了上面的命令後,會在你的主目錄(home directory)創建一個叫 ~/.gitconfig 的文件。 內容如如下格式:
[user]
name = usrname
email = usrname @gmail.com
這些屬性都是全局設置,會影響該用戶創建的每一個項目。若是你想使項目裏的屬性與前面的全局設置有區別(例如把私人郵箱地址改成工做郵箱);你能夠在項目中使用git config 命令不帶 --global 選項來設置. 這會在你項目目錄下的 .git/config 文件增長一節[user]內容(如上所示)。
設置中文字符集爲gbk
$ git config --global i18n.commitencoding gbk linux
$ git config --global i18n.logoutputencoding gbk git
初始化一個新的倉庫
首先建立一個目錄,在這個目錄下進行版本管理
$ mkdir hellogit
在這個目錄下建立空git版本庫
$git init
執行完這個命令以後,會在這個目錄下面產生一個.git目錄,全部的版本信息會保存在這個目錄之下。.git目錄下的文件顯示以下:github
其中branches目錄下面保存着全部已經創建的branch信息;
config描述了這個版本庫的全部屬性配置;
HEAD描述了當前的版本庫指向了哪一個branch;
info中包含exclude文件描述了整個版本庫中有哪些文件不須要版本控制;
refs中保存了開發過程當中所打的tag和各個tag相關聯的版本信息;
objects中保存了各個版本信息,而且以各個版本的hash值爲文件名。web
配置例外(ignore)
項目中常常會生成一些Git系統不須要追蹤(track)的文件。典型的是在編譯生成過程當中產生的文件或是編程器生成的臨時備份文件。固然,你不追蹤這些文件,能夠平時不用"git add"去把它們加到索引中。可是這樣會很快變成一件煩人的事,你發現項目中處處有未追蹤(untracked)的文件; 這樣也使"git add ." 和"git commit -a" 變得實際上沒有用處,同時"git status"命令的輸出也會有它們的信息。
你能夠在你的頂層工做目錄中添加一個叫".gitignore"的文件,來告訴Git系統要忽略掉哪些文件,下面是文件內容的示例:
# 以'#' 開始的行,被視爲註釋.
# 忽略掉全部文件名是 foo.txt 的文件.
foo.txt
# 忽略全部生成的 html 文件,
*.html
# foo.html是手工維護的,因此例外.
!foo.html
# 忽略全部.o 和 .a文件.
*.[oa]
# 忽略svn目錄
.svn/編程
你能夠把".gitignore" 這個文件放到工做樹(working tree)裏的其它目錄中,這就會在它和它的子目錄起忽略(ignore) 指定文件的做用。. gitignore文件一樣能夠像其它文件同樣加到項目倉庫裏( 直接用git add .gitignore和git commit等命令), 這樣項目裏的其它開發者也能共享同一套忽略文件規則。
若是你想忽略規則只對特定的倉庫起做用,你能夠把這些忽略規則寫到你的倉庫下 .git/info/exclude文件中,或是寫在Git配置變量core.excludesfile中指定的文件裏。有些Git命令也可在命令行參數中指定忽略規則,你能夠在這裏:gitignore查看詳細的用法。windows
Clone一個倉庫
咱們也能夠在已知一個項目版本庫的地址(Git URL)狀況下,拷貝這個版本庫到本地開發環境。Git可以支持不少連接協議的拷貝, Git URL能夠是ssh://, http(s)://, git://,或是包含有.git目錄的本地路徑。在一個新的目錄下須要拷貝剛纔版本庫,可使用:
$git clone ~/hellogit
對於遠程的版本庫,能夠經過不僅一種協議來訪問,例如,Git自己的源代碼你既能夠用 git:// 協議來訪問:
$ git clone git://git.kernel.org/pub/scm/git/git.git
也能夠經過http 協議來訪問:
$ git clone http://www.kernel.org/pub/scm/git/git.git
git://協議較爲快速和有效,可是有時必須使用http協議或者ssh協議,以ssh協議爲例,你首先須要在當前用戶目錄下的.ssh文件夾中放有連接對方ssh服務器的公鑰和私鑰,而後經過命令
$ git clone ssh://usrname@IP:PORT/home/szh/hellogit
在實際應用中,爲了方即可以添加如下命令
$ git remote add alias_hellogit_path ssh://name@ip:port/<gitpath>bash
上面的命令就會增長ssh地址別名爲alias_hellogit_path的遠程服務器,之後提交代碼的時候只須要使用 alias_hellogit_path別名便可,此時在.git/config會有以下內容出現:
[remote " alias_hellogit_path"]
ssh://name@ip:port/<gitpath>
fetch = +refs/heads/*:refs/remotes/ alias_hellogit_path /*
通過alias設置,能夠直接經過一下命令進行clone
$ git clone alias_hellogit_path服務器
追蹤更新
創建了版本倉庫以後,就能夠跟蹤這個倉庫下面的全部改動。首先建立三個文件file1, file2, file3,對其中文件進行修改。markdown
若是須要將更新的內容添進行跟蹤,使用以下指令將它們更新的內容添加到索引中.
$ git add file1 file2 file3
你如今爲commit作好了準備,你可使用git diff命令再加上 --cached 參數 ,看看哪些文件將被提交(commit)。
$ git diff --cached
(若是沒有--cached參數,git diff 會顯示當前你全部已作的但沒有加入到索引裏的修改.) 你也能夠用git status命令來得到當前項目的一個情況:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: file1
# modified: file2
# modified: file3
若是你要作進一步的修改, 那就繼續作, 作完後就把新修改的文件經過add加入到索引中. 最後把他們提交:
$ git commit
這會提示你輸入本次修改的註釋,完成後就會記錄一個新的項目版本.
除了用git add命令,我還能夠用
$ git commit -a
這會自動把全部內容被修改的文件(不包括新建立的文件)都添加到索引中,而且同時把它們提交。
hint: commit註釋最好以#issue_id爲開頭,再附上這個issue的簡要描述;而後空一行再把詳細的註釋寫清楚。這樣就能夠很方便的用工具來檢索工程中每一個開發單的進度.
Git跟蹤的是內容不是文件
不少版本控制系統都提供了一個 "add" 命令:告訴系統開始去跟蹤某一個文件的改動。可是Git裏的 」add」 命令從某種程度上講更爲簡單和強大. git add不可是用來添加不在版本控制中的新文件,也用於添加已在版本控制中可是剛修改過的文件; 在這兩種狀況下, Git都會得到當前文件的快照而且把內容暫存(stage)到索引中,爲下一次commit作好準備。
撒銷操做
在項目的開發過程當中,誤操做或者需求臨時變動都會要求撤銷以前的提交,Git提供了多種方法來實現這些操做。git對add和commit的撤銷也是不相同的。
1)對未跟蹤內容的撤銷
若是你修改了test1這個文件,但未將這些改動跟蹤到了版本庫(未進行add操做),這時只須要輸入如下命令,就能夠回退:
$git checkout test1
若是須要回退全部文件,能夠直接輸入:
$git checkout .
2)add 撤銷
若是你修改了test1這個文件,而且將這些改動跟蹤到了版本庫(只進行了add操做,沒有進行commit操做),最簡單的操做就是先 刪除這個文件,而後輸入如下命令:
$ git checkout test1
若是跟蹤的文件比較多,刪除文件比較繁瑣,則能夠選擇如下命令
$ git reset --hard HEAD
這裏的HEAD指該分支中最新的一次commit內容,該命令將工程裏的全部文件恢復到版本庫裏最近一次提交的內容。
3) commit的撤銷
若是想撤銷已經commit的內容,可使用的git的revert或者reset命令。假設版本庫裏面有3次版本提交,分別爲c1, c2, c3;有了一次版本跟蹤,爲a1;還有一些本地未跟蹤的修改,爲m1,版本庫信息以下圖所示。
下面將講述如何使用reset和revert來撤銷c2的提交.
revert撤銷
revert命令不容許用戶的本地版本庫中有任何未提交的修改,因此若是強行輸入revert命令,git將提示以下信息:
error: Your local changes would be overwritten by revert.
hint: Commit your changes or stash them to proceed.
fatal: revert failed
因此在revert以前,你必須將a1和m1進行提交或者撤銷。假使咱們撤銷了a1和m1以後使用命令:
$ git revert HEAD~2
其中命令最後的數字表示在版本倉庫中向後回退版本的個數。你也能夠指定具體的版本id進行回退,如
$git revert $COMMIT _CODE
此時git會將撤銷的c2內容與最新c3內容進行merge,併產生版本號c4', 此時版本庫信息以下圖所示:
reset撤銷
$ git reset --soft HEAD~2
這時在版本庫中已經沒有了c2,c3兩次提交的內容,可是原文件test1的內容與撤銷以前的內容一致,a1的跟蹤信息和m1的修改內容還都存在,reset以後的版本信息如圖所示:
$git reset --mixed HEAD~2
這也是reset命令在沒有參數狀況下的默認操做,這個命令以後版本庫中已經沒有了c2,c3兩次提交的內容,a1的跟蹤信息也被撤銷,可是原文件test1的內容與撤銷以前的內容一致,a1和m1的修改內容還都存在,reset以後的版本信息如圖所示:
$git reset --hard HEAD~2
這個命令以後版本庫中已經沒有了c2,c3兩次提交的內容,原文件test1的內容也變回了與c1版本一致,a1的跟蹤信息和m1的修改內容都丟失,reset以後的版本信息如圖所示:
比較(diff)
你能夠用git diff來比較項目中任意兩個版本的差別。
$ git diff master..test
上面這條命令只顯示兩個分支間的差別,若是你想找出‘master’,‘test’的共有父分支和'test'分支之間的差別,你用3個‘.'來取代前面的兩個'.' 。
$ git diff master...test
git diff是一個難以置信的有用的工具,能夠找出你項目上任意兩點間的改動,或是用來查看別人提交進來的新分支。
哪些內容會被提交(commit)
你一般用git diff來找你當前工做目錄中上次提交與本地索引間的差別。
$ git diff
上面的命令會顯示在當前的工做目錄裏沒有被追蹤,且在下次提交時不會被提交的修改。
$ git diff HEAD~$NUM HEAD~$NUM
上面的命令會顯示在當前的工做目錄中兩個版本號之間的差異。
若是你要看在下次提交時要提交的內容),你能夠運行:
$ git diff --cached
上面的命令會顯示你當前的索引和上次提交間的差別;這些內容在不帶"-a"參數運行 "git commit"命令時就會被提交。
$ git diff HEAD
上面這條命令會顯示你工做目錄與上次提交時之間的全部差異,這條命令所顯示的內容都會在執行"git commit -a"命令時被提交。
$ git diff HEAD -- ./lib
上面這條命令會顯示你當前工做目錄下的lib目錄與上次提交之間的差異(或者更準確的說是在當前分支)。
若是不是查看每一個文件的詳細差異,而是統計一下有哪些文件被改動,有多少行被改動,就可使用‘--stat' 參數。
$>git diff --stat
layout/book_index_template.html | 8 ++-
text/05_Installing_Git/0_Source.markdown | 14 ++++++
text/05_Installing_Git/1_Linux.markdown | 17 +++++++
text/05_Installing_Git/2_Mac_104.markdown | 11 +++++
text/05_Installing_Git/3_Mac_105.markdown | 8 ++++
text/05_Installing_Git/4_Windows.markdown | 7 +++
.../1_Getting_a_Git_Repo.markdown | 7 +++-
.../0_ Comparing_Commits_Git_Diff.markdown | 45 +++++++++++++++++++-
.../0_ Hosting_Git_gitweb_repoorcz_github.markdown | 4 +-
9 files changed, 115 insertions(+), 6 deletions(-)
有時這樣全局性的查看哪些文件被修改,能讓你更輕輕一點。
建立分支
一個Git倉庫能夠維護不少開發分支。如今咱們來建立一個新的叫」experimental」的分支:
$ git branch experimental
此時你運行下面這條命令:
$ git branch
你會獲得當前倉庫中存在的全部分支列表:
experimental
* master
「experimental」 分支是你剛纔建立的,「master」分支是Git系統默認建立的主分支。星號(「*」)標識了你當工做在哪一個分支下,
切換分支
$ git checkout experimental
切換到」experimental」分支,先編輯裏面的一個文件,再提交(commit)改動,最後切換回 「master」分支。
$ git commit -a
$ git checkout master
你如今能夠看一下你原來在「experimental」分支下所做的修改還在不在;由於你如今切換回了「master」分支,因此原來那些修改就不存在了。
你如今能夠在「master」分支下再做一些不一樣的修改:
$ git commit -a
這時,兩個分支就有了各自不一樣的修改(diverged);咱們能夠經過下面的命令來合併「experimental」和「master」兩個分支:
$ git merge -no-ff experimental
默認狀況下,Git執行」快進式合併」(fast-farward merge),會直接將Master分支指向Develop分支。
使用–no–ff參數後,會執行正常合併,在Master分支上生成一個新節點。爲了保證版本log的清晰,推薦採用這種作法。
若是這個兩個分支間的修改沒有衝突(conflict), 那麼合併就完成了。若有有衝突,會有以下相似的信息顯示
100% (4/4) done
Auto-merged file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
輸入下面的命令就能夠查看當前有哪些文件產生了衝突:
$ git diff
若是執行自動合併無成功的話,git會在索引和工做樹裏設置一個特殊的狀態,提示你如何解決合併中出現的衝突。
有衝突(conflicts)的文件會保存在索引中,除非你解決了問題了而且更新了索引,不然執行git commit都會失敗:
$ git commit
file.txt: needs merge
若是執行git status會顯示這些文件沒有合併(unmerged),這些有衝突的文件裏面會添加像下面的衝突標識符:
<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
你所須要的作是就是編輯解決衝突,(接着把衝突標識符刪掉),再執行下面的命令:
$ git add file.txt
$ git commit
注意:提交註釋裏已經有一些關於合併的信息了,一般是用這些默認信息,可是你能夠添加一些你想要的註釋。
這時你就能夠刪除掉你的 「experimental」 分支了(若是願意):
$ git branch -d experimental
git branch -d只能刪除那些已經被當前分支合併的分支. 若是你要強制刪除某個分支的話就用git branch –D;下面假設你要強制刪除一個叫」crazy-idea」的分支:
$ git branch -D crazy-idea
假設Alice如今開始了一個新項目,在/home/alice/project建了一個新的git 倉庫(repository);另外一個叫Bob的工做目錄也在同一臺機器,他要提交代碼。
Bob 執行了這樣的命令:
$ git clone /home/alice/project myrepo
這就建了一個新的叫"myrepo"的目錄,這個目錄裏包含了一份Alice的倉庫的克隆(clone). 這份克隆和原始的項目如出一轍,而且擁有原始項目的歷史記錄。
Bob 作了一些修改而且提交(commit)它們:
$ git commit -a
(repeat as necessary)
當他準備好了,他告訴Alice從倉庫/home/bob/myrepo中把他的修改給拉 (pull)下來。她執行了下面幾條命令:
$ cd /home/alice/project
$ git pull /home/bob/myrepo master
這就把Bob的主(master)分支合併到了Alice的當前分支裏了。若是Alice在 Bob修改文件內容的同時也作了修改的話,她可能須要手工去修復衝突. (注意:"master"參數在上面的命令中並不必定是必須的,由於這是一個默認參數)
git pull命令執行兩個操做: 它從遠程分支(remote branch)抓取修改的內容,而後把它合併進當前的分支。
若是你要常常操做遠程分支(remote branch),你能夠定義它們的縮寫:
$ git remote add bob /home/bob/myrepo
這樣,Alic能夠用"git fetch"" 來執行"git pull"前半部分的工做,可是這條命令並不會把抓下來的修改合併到當前分支裏。
$ git fetch bob
咱們用git remote命令創建了Bob的運程倉庫的縮寫,用這個(縮寫) 名字我從Bob那獲得全部遠程分支的歷史記錄。在這裏遠程分支的名字就叫bob/master.
$ git log -p master..bob/master
上面的命令把Bob從Alice的主分支(master)中籤出後所作的修改所有顯示出來。
當檢查完修改後,Alice就能夠把修改合併到她的主分支中。
$ git merge bob/master
這種合併(merge)也能夠用pull來完成,就像下面的命令同樣:
$ git pull . remotes/bob/master
注意:git pull 會把遠程分支合併進當前的分支裏,而無論你在命令行裏指定什麼。
其後,Bob能夠更新它的本地倉庫--把Alice作的修改拉過來(pull):
$ git pull
若是Bob從Alice的倉庫克隆(clone),那麼他就不須要指定Alice倉庫的地址;由於Git把Alice倉庫的地址存儲到Bob的倉庫配庫文件,這個地址就是在git pull時使用:
$ git config --get remote.origin.url
/home/alice/project
(若是要查看git clone建立的全部配置參數,可使用"git config -l", git config的幫助文件裏解釋了每一個參數的含義.)
Git同時也保存了一份最初(pristine)的Alice主分支(master),在 "origin/master"下面。
$ git branch -r
origin/master
若是Bob打算在另一臺主機上工做,他能夠經過ssh協議來執行"clone" 和"pull"操做:
$ git clone alice.org:/home/alice/project myrepo
一旦涉及多人在不一樣的工做目錄下協同開發時,須要創建一箇中央版本倉庫,由於分佈式的版本倉庫一旦checkout以後,就不能被其餘開發者主動執行push操做。下圖就是一個多系統下多人合做協同開發的工做目錄示意圖,user1和user2須要一塊兒合做開發一個功能,爲了不在開發完成以前就提交svn主版本庫,能夠運用git來進行小規模的代碼同步,同時user1和user2又須要在linux和windows下面進行開發,也須要時時同步代碼。爲了完成這個需求,能夠在windows和linux下各創建一個git工做目錄,而後在任一臺機子創建一個git中央版本倉庫,將全部工做目錄指向這個中央版本倉庫。具體步驟以下:
你仍是天天在你的本地私人倉庫裏工做,可是會按期的把本地的修改push到你的公開倉庫中;其它開發者就能夠從這個公開倉庫pull最新的代碼。
在linux系統中建立中央版本倉庫(repository),在一個空的文件夾下,輸入命令:
$git --bare init
--bare 在這裏是產生一個乾淨的中央版本倉庫,僅包含版本歷史記錄不包含工做代碼和工做目錄,相似於svn服務器,這樣的版本倉庫能夠被開發者主動push修改。
建立私有版本倉庫,在我的文件夾下,輸入命令:
$ git init
這樣就爲咱們須要版本管理的代碼建立了一個私有的分佈式版本倉庫,這個版本倉庫跟咱們平時在用版本倉庫沒有兩樣,你能夠經過命令add,commit和reset進行版本管理。
在本地倉庫添加遠程倉庫路徑,輸入命令:
$git remote add origin $CENTER_REPOSITORY_PATH
其中origin是版本庫路徑的別名,$CENTER_REPOSITORY_PATH
是中央版本倉庫的路徑,在之後的操做中能夠用origin來代替。
$git push origin $BRANCH_NAME
這個命令將私有版本中的工做代碼和版本歷史記錄添加到中央版本倉庫中,其中包括這個私有版本倉庫中全部的修改提交記錄。
這個命令以後本地目錄下的 .git/config會發生改變。
windows端checkout出代碼,在msysGit的bash中,輸入命令:
$ git clone origin
由於咱們的服務器上的ssh協議只容許公鑰/私鑰遠程登入,因此在執行這個命令以前須要將公私鑰放到~/.ssh的目錄下面。
在完成這個步驟以後,在windows和linux下已經各有一個獨立的私有版本倉庫,它們的地位是相同的,二者經過中央版本倉庫進行代碼同步。
此刻對各自私有版本倉庫的commit,add和revert等操做只是對本私有版本倉庫可見,修改歷史對其餘版本庫不可見。
多人協同 user1作了些修改,而後提交修改到中央版本庫 $git push origin $BRANCH_NAME 其中$BRANCH_NAME指代功能開發的分支。 user2要同步user1的修改 $ git fetch origin $BRANCH_NAME 這個是抓取一個遠程的版本庫中的信息到一個私有版本庫中,但不更新代碼。通過這個操做能夠經過命令git log看到遠程版本庫中的全部操做。 $ git pull origin $BRANCH_NAME 這個命令真正將將user1的修改merge到當前的版本庫。 通過這個操,user2已經有了user1的全部修改和提交記錄。