1. 程式人生 > >git進階

git進階

details mas 自由 http 輸入 byte 就會 secret 成功

git進階

1 分支管理

分支就是科幻電影裏面的平行宇宙,當你正在電腦前努力學習Git的時候,另一個你正在另一個平行宇宙裏努力學習SVN。

如果兩個平行宇宙互不幹擾,那對現在的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合並了,結果,你既學會了Git又學會了SVN!

技術分享

分支在實際中有什麽用呢?假設你準備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由於代碼還沒寫完,不完整的代碼庫會導致別人不能幹活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。

現在有了分支,就不用怕了。你創建了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上幹活,想提交就提交,直到開發完畢後,再一次性合並到原來的分支上,這樣,既安全,又不影響別人工作。

其他版本控制系統如SVN等都有分支管理,但是用過之後你會發現,這些版本控制系統創建和切換分支比蝸牛還慢,簡直讓人無法忍受,結果分支功能成了擺設,大家都不去用。

但Git的分支是與眾不同的,無論創建、切換和刪除分支,Git在1秒鐘之內就能完成!無論你的版本庫是1個文件還是1萬個文件。

1.1 創建與合並分支

初識git—版本回退裏,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裏,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD

指向的就是當前分支。

一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:

技術分享

每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長:

當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:

技術分享

你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!

不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次後,dev

指針往前移動一步,而master指針不變:

技術分享

假如我們在dev上的工作完成了,就可以把dev合並到master上。Git怎麽合並呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合並:

技術分享

所以Git合並分支也很快!就改改指針,工作區內容也不變!

合並完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉後,我們就剩下了一條master分支:

技術分享

真是太神奇了,你看得出來有些提交是通過分支完成的嗎?

下面開始實戰。

首先,我們創建dev分支,然後切換到dev分支:

``` $ git checkout -b dev Switched to a new branch ‘dev‘

```

git checkout命令加上-b參數表示創建並切換,相當於以下兩條命令:

``` $ git branch dev $ git checkout dev Switched to branch ‘dev‘

```

然後,用git branch命令查看當前分支:

``` $ git branch * dev master

```

git branch命令會列出所有分支,當前分支前面會標一個*號。

然後,我們就可以在dev分支上正常提交,比如對readme.txt做個修改,加上一行:

``` Creating a new branch is quick.

```

然後提交:

``` $ git add readme.txt $ git commit -m "branch test" [dev fec145a] branch test 1 file changed, 1 insertion(+)

```

現在,dev分支的工作完成,我們就可以切換回master分支:

``` $ git checkout master Switched to branch ‘master‘

```

切換回master分支後,再查看一個readme.txt文件,剛才添加的內容不見了!因為那個提交是在dev分支上,而master分支此刻的提交點並沒有變:

技術分享

現在,我們把dev分支的工作成果合並到master分支上:

``` $ git merge dev Updating d17efd8..fec145a Fast-forward readme.txt | 1 + 1 file changed, 1 insertion(+)

```

git merge命令用於合並指定分支到當前分支。合並後,再查看readme.txt的內容,就可以看到,和dev分支的最新提交是完全一樣的。

註意到上面的Fast-forward信息,Git告訴我們,這次合並是“快進模式”,也就是直接把master指向dev的當前提交,所以合並速度非常快。

當然,也不是每次合並都能Fast-forward,我們後面會講其他方式的合並。

合並完成後,就可以放心地刪除dev分支了:

``` $ git branch -d dev Deleted branch dev (was fec145a).

```

刪除後,查看branch,就只剩下master分支了:

``` $ git branch * master

```

因為創建、合並和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合並後再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。

1.2 小結

Git鼓勵大量使用分支:

  • 查看分支:git branch
  • 創建分支:git branch
  • 切換分支:git checkout
  • 創建+切換分支:git checkout -b
  • 合並某分支到當前分支:git merge
  • 刪除分支:git branch -d

2 解決沖突

人生不如意之事十之八九,合並分支往往也不是一帆風順的。

準備新的feature1分支,繼續我們的新分支開發:

``` $ git checkout -b feature1 Switched to a new branch ‘feature1‘

```

修改readme.txt最後一行,改為:

``` Creating a new branch is quick AND simple.

```

feature1分支上提交:

``` $ git add readme.txt $ git commit -m "AND simple" [feature1 75a857c] AND simple 1 file changed, 1 insertion(+), 1 deletion(-)

```

切換到master分支:

``` $ git checkout master Switched to branch ‘master‘ Your branch is ahead of ‘origin/master‘ by 1 commit.

```

Git還會自動提示我們當前master分支比遠程的master分支要超前1個提交。

master分支上把readme.txt文件的最後一行改為:

``` Creating a new branch is quick & simple.

```

提交:

``` $ git add readme.txt $ git commit -m "& simple" [master 400b400] & simple 1 file changed, 1 insertion(+), 1 deletion(-)

```

現在,master分支和feature1分支各自都分別有新的提交,變成了這樣:

技術分享

這種情況下,Git無法執行“快速合並”,只能試圖把各自的修改合並起來,但這種合並就可能會有沖突,我們試試看:

``` $ git merge feature1 Auto-merging readme.txt CONFLICT (content): Merge conflict in readme.txt Automatic merge failed; fix conflicts and then commit the result.

```

果然沖突了!Git告訴我們,readme.txt文件存在沖突,必須手動解決沖突後再提交。git status也可以告訴我們沖突的文件:

``` $ git status

On branch master

Your branch is ahead of ‘origin/master‘ by 2 commits.

Unmerged paths:

(use "git add/rm ..." as appropriate to mark resolution)

both modified: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

```

我們可以直接查看readme.txt的內容:

``` Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. <<<<<<< HEAD

Creating a new branch is quick & simple.

Creating a new branch is quick AND simple.

feature1

```

Git用<<<<<<<=======>>>>>>>標記出不同分支的內容,我們修改如下後保存:

``` Creating a new branch is quick and simple.

```

再提交:

``` $ git add readme.txt $ git commit -m "conflict fixed" [master 59bc1cb] conflict fixed

```

現在,master分支和feature1分支變成了下圖所示:

技術分享

用帶參數的git log也可以看到分支的合並情況:

``` $ git log --graph --pretty=oneline --abbrev-commit * 59bc1cb conflict fixed |\ | * 75a857c AND simple * | 400b400 & simple |/ * fec145a branch test ...

```

最後,刪除feature1分支:

``` $ git branch -d feature1 Deleted branch feature1 (was 75a857c).

```

工作完成。

小結

當Git無法自動合並分支時,就必須首先解決沖突。解決沖突後,再提交,合並完成。

git log --graph命令可以看到分支合並圖。

3 分支管理策略

通常,合並分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支後,會丟掉分支信息。

如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。

下面我們實戰一下--no-ff方式的git merge

首先,仍然創建並切換dev分支:

``` $ git checkout -b dev Switched to a new branch ‘dev‘

```

修改readme.txt文件,並提交一個新的commit:

``` $ git add readme.txt $ git commit -m "add merge" [dev 6224937] add merge 1 file changed, 1 insertion(+)

```

現在,我們切換回master

``` $ git checkout master Switched to branch ‘master‘

```

準備合並dev分支,請註意--no-ff參數,表示禁用Fast forward

``` $ git merge --no-ff -m "merge with no-ff" dev Merge made by the ‘recursive‘ strategy. readme.txt | 1 + 1 file changed, 1 insertion(+)

```

因為本次合並要創建一個新的commit,所以加上-m參數,把commit描述寫進去。

合並後,我們用git log看看分支歷史:

``` $ git log --graph --pretty=oneline --abbrev-commit * 7825a50 merge with no-ff |\ | * 6224937 add merge |/ * 59bc1cb conflict fixed ...

```

可以看到,不使用Fast forward模式,merge後就像這樣:

技術分享

分支策略

在實際開發中,我們應該按照幾個基本原則進行分支管理:

首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面幹活;

那在哪幹活呢?幹活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合並到master上,在master分支發布1.0版本;

你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合並就可以了。

所以,團隊合作的分支看起來就像這樣:

技術分享

小結

Git分支十分強大,在團隊開發中應該充分應用。

合並分支時,加上--no-ff參數就可以用普通模式合並,合並後的歷史有分支,能看出來曾經做過合並,而fast forward合並就看不出來曾經做過合並

4 多人協作

當你從遠程倉庫克隆時,實際上Git自動把本地的master分支和遠程的master分支對應起來了,並且,遠程倉庫的默認名稱是origin

要查看遠程庫的信息,用git remote

``` $ git remote origin

```

或者,用git remote -v顯示更詳細的信息:

``` $ git remote -v origin [email protected]:michaelliao/learngit.git (fetch) origin [email protected]:michaelliao/learngit.git (push)

```

上面顯示了可以抓取和推送的origin的地址。如果沒有推送權限,就看不到push的地址。

推送分支

推送分支,就是把該分支上的所有本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:

``` $ git push origin master

```

如果要推送其他分支,比如dev,就改成:

``` $ git push origin dev

```

但是,並不是一定要把本地分支往遠程推送,那麽,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要時刻與遠程同步;
  • dev分支是開發分支,團隊所有成員都需要在上面工作,所以也需要與遠程同步;
  • bug分支只用於在本地修復bug,就沒必要推到遠程了,除非老板要看看你每周到底修復了幾個bug;
  • feature分支是否推到遠程,取決於你是否和你的小夥伴合作在上面開發。

總之,就是在Git中,分支完全可以在本地自己藏著玩,是否推送,視你的心情而定!

抓取分支

多人協作時,大家都會往masterdev分支上推送各自的修改。

現在,模擬一個你的小夥伴,可以在另一臺電腦(註意要把SSH Key添加到GitHub)或者同一臺電腦的另一個目錄下克隆:

``` $ git clone [email protected]:michaelliao/learngit.git Cloning into ‘learngit‘... remote: Counting objects: 46, done. remote: Compressing objects: 100% (26/26), done. remote: Total 46 (delta 16), reused 45 (delta 15) Receiving objects: 100% (46/46), 15.69 KiB | 6 KiB/s, done. Resolving deltas: 100% (16/16), done.

```

當你的小夥伴從遠程庫clone時,默認情況下,你的小夥伴只能看到本地的master分支。不信可以用git branch命令看看:

``` $ git branch * master

```

現在,你的小夥伴要在dev分支上開發,就必須創建遠程origindev分支到本地,於是他用這個命令創建本地dev分支:

``` $ git checkout -b dev origin/dev

```

現在,他就可以在dev上繼續修改,然後,時不時地把dev分支push到遠程:

``` $ git commit -m "add /usr/bin/env" [dev 291bea8] add /usr/bin/env 1 file changed, 1 insertion(+) $ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 349 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To [email protected]:michaelliao/learngit.git fc38031..291bea8 dev -> dev

```

你的小夥伴已經向origin/dev分支推送了他的提交,而碰巧你也對同樣的文件作了修改,並試圖推送:

``` $ git add hello.py $ git commit -m "add coding: utf-8" [dev bd6ae48] add coding: utf-8 1 file changed, 1 insertion(+) $ git push origin dev To [email protected]:michaelliao/learngit.git ! [rejected] dev -> dev (non-fast-forward) error: failed to push some refs to [email protected]:michaelliao/learngit.git‘ hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. ‘git pull‘) hint: before pushing again. hint: See the ‘Note about fast-forwards‘ in ‘git push --help‘ for details.

```

推送失敗,因為你的小夥伴的最新提交和你試圖推送的提交有沖突,解決辦法也很簡單,Git已經提示我們,先用git pull把最新的提交從origin/dev抓下來,然後,在本地合並,解決沖突,再推送:

``` $ git pull remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 3 (delta 0) Unpacking objects: 100% (3/3), done. From github.com:michaelliao/learngit fc38031..291bea8 dev -> origin/dev There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details

git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream dev origin/<branch>

```

git pull也失敗了,原因是沒有指定本地dev分支與遠程origin/dev分支的鏈接,根據提示,設置devorigin/dev的鏈接:

``` $ git branch --set-upstream dev origin/dev Branch dev set up to track remote branch dev from origin.

```

再pull:

``` $ git pull Auto-merging hello.py CONFLICT (content): Merge conflict in hello.py Automatic merge failed; fix conflicts and then commit the result.

```

這回git pull成功,但是合並有沖突,需要手動解決,解決的方法和分支管理中的解決沖突完全一樣。解決後,提交,再push:

``` $ git commit -m "merge & fix hello.py" [dev adca45d] merge & fix hello.py $ git push origin dev Counting objects: 10, done. Delta compression using up to 4 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 747 bytes, done. Total 6 (delta 0), reused 0 (delta 0) To [email protected]:michaelliao/learngit.git 291bea8..adca45d dev -> dev

```

因此,多人協作的工作模式通常是這樣:

  1. 首先,可以試圖用git push origin branch-name推送自己的修改;
  2. 如果推送失敗,則因為遠程分支比你的本地更新,需要先用git pull試圖合並;
  3. 如果合並有沖突,則解決沖突,並在本地提交;
  4. 沒有沖突或者解決掉沖突後,再用git push origin branch-name推送就能成功!

如果git pull提示“no tracking information”,則說明本地分支和遠程分支的鏈接關系沒有創建,用命令git branch --set-upstream branch-name origin/branch-name

這就是多人協作的工作模式,一旦熟悉了,就非常簡單。

小結

  • 查看遠程庫信息,使用git remote -v
  • 本地新建的分支如果不推送到遠程,對其他人就是不可見的;
  • 從本地推送分支,使用git push origin branch-name,如果推送失敗,先用git pull抓取遠程的新提交;
  • 在本地創建和遠程分支對應的分支,使用git checkout -b branch-name origin/branch-name,本地和遠程分支的名稱最好一致;
  • 建立本地分支和遠程分支的關聯,使用git branch --set-upstream branch-name origin/branch-name
  • 從遠程抓取分支,使用git pull,如果有沖突,要先處理沖突。

5 標簽管理

發布一個版本時,我們通常先在版本庫中打一個標簽(tag),這樣,就唯一確定了打標簽時刻的版本。將來無論什麽時候,取某個標簽的版本,就是把那個打標簽的時刻的歷史版本取出來。所以,標簽也是版本庫的一個快照。

Git的標簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標簽不能移動),所以,創建和刪除標簽都是瞬間完成的。

Git有commit,為什麽還要引入tag?

“請把上周一的那個版本打包發布,commit號是6a5819e...”

“一串亂七八糟的數字不好找!”

如果換一個辦法:

“請把上周一的那個版本打包發布,版本號是v1.2”

“好的,按照tag v1.2查找commit就行!”

所以,tag就是一個讓人容易記住的有意義的名字,它跟某個commit綁在一起。

5.1 創建標簽

在Git中打標簽非常簡單,首先,切換到需要打標簽的分支上:

``` $ git branch * dev master $ git checkout master Switched to branch ‘master‘

```

然後,敲命令git tag就可以打一個新標簽:

``` $ git tag v1.0

```

可以用命令git tag查看所有標簽:

``` $ git tag v1.0

```

默認標簽是打在最新提交的commit上的。有時候,如果忘了打標簽,比如,現在已經是周五了,但應該在周一打的標簽沒有打,怎麽辦?

方法是找到歷史提交的commit id,然後打上就可以了:

``` $ git log --pretty=oneline --abbrev-commit 6a5819e merged bug fix 101 cc17032 fix bug 101 7825a50 merge with no-ff 6224937 add merge 59bc1cb conflict fixed 400b400 & simple 75a857c AND simple fec145a branch test d17efd8 remove test.txt ...

```

比方說要對add merge這次提交打標簽,它對應的commit id是6224937,敲入命令:

``` $ git tag v0.9 6224937

```

再用命令git tag查看標簽:

``` $ git tag v0.9 v1.0

```

註意,標簽不是按時間順序列出,而是按字母排序的。可以用git show查看標簽信息:

``` $ git show v0.9 commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4 Author: Michael Liao [email protected] Date: Thu Aug 22 11:22:08 2013 +0800

add merge

...

```

可以看到,v0.9確實打在add merge這次提交上。

還可以創建帶有說明的標簽,用-a指定標簽名,-m指定說明文字:

``` $ git tag -a v0.1 -m "version 0.1 released" 3628164

```

用命令git show可以看到說明文字:

``` $ git show v0.1 tag v0.1 Tagger: Michael Liao [email protected] Date: Mon Aug 26 07:28:11 2013 +0800

version 0.1 released

commit 3628164fb26d48395383f8f31179f24e0882e1e0 Author: Michael Liao [email protected] Date: Tue Aug 20 15:11:49 2013 +0800

append GPL

```

還可以通過-s用私鑰簽名一個標簽:

``` $ git tag -s v0.2 -m "signed version 0.2 released" fec145a

```

簽名采用PGP簽名,因此,必須首先安裝gpg(GnuPG),如果沒有找到gpg,或者沒有gpg密鑰對,就會報錯:

``` gpg: signing failed: secret key not available error: gpg failed to sign the data error: unable to sign the tag

```

如果報錯,請參考GnuPG幫助文檔配置Key。

用命令git show可以看到PGP簽名信息:

``` $ git show v0.2 tag v0.2 Tagger: Michael Liao [email protected] Date: Mon Aug 26 07:28:33 2013 +0800

signed version 0.2 released -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin)

iQEcBAABAgAGBQJSGpMhAAoJEPUxHyDAhBpT4QQIAKeHfR3bo... -----END PGP SIGNATURE-----

commit fec145accd63cdc9ed95a2f557ea0658a2a6537f Author: Michael Liao [email protected] Date: Thu Aug 22 10:37:30 2013 +0800

branch test

```

用PGP簽名的標簽是不可偽造的,因為可以驗證PGP簽名。驗證簽名的方法比較復雜,這裏就不介紹了。

小結

  • 命令git tag用於新建一個標簽,默認為HEAD,也可以指定一個commit id;
  • git tag -a -m "blablabla..."可以指定標簽信息;
  • git tag -s -m "blablabla..."可以用PGP簽名標簽;
  • 命令git tag可以查看所有標簽。

5.2 操作標簽

如果標簽打錯了,也可以刪除:

``` $ git tag -d v0.1 Deleted tag ‘v0.1‘ (was e078af9)

```

因為創建的標簽都只存儲在本地,不會自動推送到遠程。所以,打錯的標簽可以在本地安全刪除。

如果要推送某個標簽到遠程,使用命令git push origin

``` $ git push origin v1.0 Total 0 (delta 0), reused 0 (delta 0) To [email protected]:michaelliao/learngit.git * [new tag] v1.0 -> v1.0

```

或者,一次性推送全部尚未推送到遠程的本地標簽:

``` $ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 554 bytes, done. Total 1 (delta 0), reused 0 (delta 0) To [email protected]:michaelliao/learngit.git * [new tag] v0.2 -> v0.2 * [new tag] v0.9 -> v0.9

```

如果標簽已經推送到遠程,要刪除遠程標簽就麻煩一點,先從本地刪除:

``` $ git tag -d v0.9 Deleted tag ‘v0.9‘ (was 6224937)

```

然後,從遠程刪除。刪除命令也是push,但是格式如下:

``` $ git push origin :refs/tags/v0.9 To [email protected]:michaelliao/learngit.git - [deleted] v0.9

```

要看看是否真的從遠程庫刪除了標簽,可以登陸GitHub查看。

小結

  • 命令git push origin可以推送一個本地標簽;
  • 命令git push origin --tags可以推送全部未推送過的本地標簽;
  • 命令git tag -d可以刪除一個本地標簽;
  • 命令git push origin :refs/tags/可以刪除一個遠程標簽。

6 使用GitHub

我們一直用GitHub作為免費的遠程倉庫,如果是個人的開源項目,放到GitHub上是完全沒有問題的。其實GitHub還是一個開源協作社區,通過GitHub,既可以讓別人參與你的開源項目,也可以參與別人的開源項目。

在GitHub出現以前,開源項目開源容易,但讓廣大人民群眾參與進來比較困難,因為要參與,就要提交代碼,而給每個想提交代碼的群眾都開一個賬號那是不現實的,因此,群眾也僅限於報個bug,即使能改掉bug,也只能把diff文件用郵件發過去,很不方便。

但是在GitHub上,利用Git極其強大的克隆和分支功能,廣大人民群眾真正可以第一次自由參與各種開源項目了。

如何參與一個開源項目呢?比如人氣極高的bootstrap項目,這是一個非常強大的CSS框架,你可以訪問它的項目主頁https://github.com/twbs/bootstrap,點“Fork”就在自己的賬號下克隆了一個bootstrap倉庫,然後,從自己的賬號下clone:

``` git clone [email protected]:michaelliao/bootstrap.git

```

一定要從自己的賬號下clone倉庫,這樣你才能推送修改。如果從bootstrap的作者的倉庫地址[email protected]:twbs/bootstrap.git克隆,因為沒有權限,你將不能推送修改。

Bootstrap的官方倉庫twbs/bootstrap、你在GitHub上克隆的倉庫my/bootstrap,以及你自己克隆到本地電腦的倉庫,他們的關系就像下圖顯示的那樣:

技術分享

如果你想修復bootstrap的一個bug,或者新增一個功能,立刻就可以開始幹活,幹完後,往自己的倉庫推送。

如果你希望bootstrap的官方庫能接受你的修改,你就可以在GitHub上發起一個pull request。當然,對方是否接受你的pull request就不一定了。

小結

  • 在GitHub上,可以任意Fork開源倉庫;
  • 自己擁有Fork後的倉庫的讀寫權限;
  • 可以推送pull request給官方倉庫來貢獻代碼。

7 自定義git

在安裝Git一節中,我們已經配置了user.nameuser.email,實際上,Git還有很多可配置項。

比如,讓Git顯示顏色,會讓命令輸出看起來更醒目:

``` $ git config --global color.ui true

```

這樣,Git會適當地顯示不同的顏色,比如git status命令:

技術分享

文件名就會標上顏色。

7.1 忽略指定文件

有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數據庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files ...,有強迫癥的童鞋心裏肯定不爽。

好在Git考慮到了大家的感受,這個問題解決起來也很簡單,在Git工作區的根目錄下創建一個特殊的.gitignore文件,然後把要忽略的文件名填進去,Git就會自動忽略這些文件。

不需要從頭寫.gitignore文件,GitHub已經為我們準備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore

忽略文件的原則是:

  1. 忽略操作系統自動生成的文件,比如縮略圖等;
  2. 忽略編譯生成的中間文件、可執行文件等,也就是如果一個文件是通過另一個文件自動生成的,那自動生成的文件就沒必要放進版本庫,比如Java編譯產生的.class文件;
  3. 忽略你自己的帶有敏感信息的配置文件,比如存放口令的配置文件。

舉個例子:

假設你在Windows下進行Python開發,Windows會自動在有圖片的目錄下生成隱藏的縮略圖文件,如果有自定義目錄,目錄下就會有Desktop.ini文件,因此你需要忽略Windows自動生成的垃圾文件:

```

Windows:

Thumbs.db ehthumbs.db Desktop.ini

```

然後,繼續忽略Python編譯產生的.pyc.pyodist等文件或目錄:

```

Python:

*.py[cod] *.so *.egg *.egg-info dist build

```

加上你自己定義的文件,最終得到一個完整的.gitignore文件,內容如下:

```

Windows:

Thumbs.db ehthumbs.db Desktop.ini

Python:

*.py[cod] *.so *.egg *.egg-info dist build

My configurations:

db.ini deploykeyrsa

```

最後一步就是把.gitignore也提交到Git,就完成了!當然檢驗.gitignore的標準是git status命令是不是說working directory clean

使用Windows的童鞋註意了,如果你在資源管理器裏新建一個.gitignore文件,它會非常弱智地提示你必須輸入文件名,但是在文本編輯器裏“保存”或者“另存為”就可以把文件保存為.gitignore了。

有些時候,你想添加一個文件到Git,但發現添加不了,原因是這個文件被.gitignore忽略了:

``` $ git add App.class The following paths are ignored by one of your .gitignore files: App.class Use -f if you really want to add them.

```

如果你確實想添加該文件,可以用-f強制添加到Git:

``` $ git add -f App.class

```

或者你發現,可能是.gitignore寫得有問題,需要找出來到底哪個規則寫錯了,可以用git check-ignore命令檢查:

``` $ git check-ignore -v App.class .gitignore:3:*.class App.class

```

Git會告訴我們,.gitignore的第3行規則忽略了該文件,於是我們就可以知道應該修訂哪個規則。

小結

  • 忽略某些文件時,需要編寫.gitignore
  • .gitignore文件本身要放到版本庫裏,並且可以對.gitignore做版本管理!

7.2 配置別名

有沒有經常敲錯命令?比如git statusstatus這個單詞真心不好記。

如果敲git st就表示git status那就簡單多了,當然這種偷懶的辦法我們是極力贊成的。

我們只需要敲一行命令,告訴Git,以後st就表示status

``` $ git config --global alias.st status

```

好了,現在敲git st看看效果。

當然還有別的命令可以簡寫,很多人都用co表示checkoutci表示commitbr表示branch

``` $ git config --global alias.co checkout $ git config --global alias.ci commit $ git config --global alias.br branch

```

以後提交就可以簡寫成:

``` $ git ci -m "bala bala bala..."

```

--global參數是全局參數,也就是這些命令在這臺電腦的所有Git倉庫下都有用。

在撤銷修改一節中,我們知道,命令git reset HEAD file可以把暫存區的修改撤銷掉(unstage),重新放回工作區。既然是一個unstage操作,就可以配置一個unstage別名:

``` $ git config --global alias.unstage ‘reset HEAD‘

```

當你敲入命令:

``` $ git unstage test.py

```

實際上Git執行的是:

``` $ git reset HEAD test.py

```

配置一個git last,讓其顯示最後一次提交信息:

``` $ git config --global alias.last ‘log -1‘

```

這樣,用git last就能顯示最近一次的提交:

``` $ git last commit adca45d317e6d8a4b23f9811c3d7b7f0f180bfe2 Merge: bd6ae48 291bea8 Author: Michael Liao [email protected]Date: Thu Aug 22 22:49:22 2013 +0800

merge & fix hello.py

```

甚至還有人喪心病狂地把lg配置成了:

``` git config --global alias.lg "log --color --graph --pretty=format:‘%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset‘ --abbrev-commit"

```

來看看git lg的效果:

技術分享

為什麽不早點告訴我?別激動,咱不是為了多記幾個英文單詞嘛!

配置文件

配置Git的時候,加上--global是針對當前用戶起作用的,如果不加,那只針對當前的倉庫起作用。

配置文件放哪了?每個倉庫的Git配置文件都放在.git/config文件中:

``` $ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = [email protected]:michaelliao/learngit.git fetch = +refs/heads/:refs/remotes/origin/ [branch "master"] remote = origin merge = refs/heads/master [alias] last = log -1

```

別名就在[alias]後面,要刪除別名,直接把對應的行刪掉即可。

而當前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:

``` $ cat .gitconfig [alias] co = checkout ci = commit br = branch st = status [user] name = Your Name email = [email protected]

```

配置別名也可以直接修改這個文件,如果改錯了,可以刪掉文件重新通過命令配置。

小結

給Git配置好別名,就可以輸入命令時偷個懶。我們鼓勵偷懶。

參考資料:廖雪峰的官方網站

git進階