內(nèi)容提要
- 忽略文件
- 忽略目錄的四種不同方式
/mytmp
/mytmp/*
**/mytmp
**/mytmp/*
- 例外
- 用
!
表示例外; - 作用于
/mytmp/*
,不能作用于/mytmp
- ignore 生效的前提
- 通配符語(yǔ)法
- 一個(gè)星表示任意字符;
- 兩個(gè)星表示任意路徑。
- 兩種形式:共享式和獨(dú)享式
- 官方模板
語(yǔ)法
當(dāng)我們進(jìn)行代碼開(kāi)發(fā)時(shí),把代碼存到遠(yuǎn)程代碼庫(kù)上。但是總有一些代碼是不需要上傳的,比如編譯的中間文件,單元測(cè)試自動(dòng)生成的測(cè)試報(bào)告等。這個(gè)時(shí)候我們需要告訴git
,哪些文件應(yīng)該忽略,git
提供了這種機(jī)制,它通過(guò).gitignore
配置文件來(lái)實(shí)現(xiàn)。通常該文件放在項(xiàng)目的根目錄下,我們可以手動(dòng)創(chuàng)建它,然后編輯內(nèi)容。
忽略文件
在 .gitignore
文件中編輯:
#.gitignore for java
*.class
第一行以#
開(kāi)頭的是注釋?zhuān)?code>*.class 表示忽略“所有”以.class
為后綴的文件(其中*
號(hào)表示glob模式匹配的通配符)。這里的“所有”無(wú)論它在哪個(gè)目錄下。
實(shí)驗(yàn)驗(yàn)證下,創(chuàng)建多級(jí)子目錄,每個(gè)目錄創(chuàng)建一個(gè).class
文件,結(jié)構(gòu)如下:
? demo-gitignore git:(master) tree
.
├── L1.class
└── child1
├── L2.class
└── child2
├── L3.class
└── child3
└── L4.class
3 directories, 4 files
執(zhí)行git status
,看看有沒(méi)有被忽略?
? demo-gitignore git:(master) git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
當(dāng)然也可以不用通配符,例如
# project specified gitignore
Hello.xml
表示忽略“所有”名字叫Hello.xml
的文件。
忽略目錄
語(yǔ)法上,以/
開(kāi)頭的表示忽略目錄。比如/mytmp
表示忽略“根目錄下”名叫mytmp
的目錄,并非表示“所有”。
在上述3個(gè)childX目錄下,各自創(chuàng)建一個(gè)mytmp
子目錄(實(shí)驗(yàn)時(shí)請(qǐng)勿用tmp
,以免用戶(hù)目錄下的~/.gitignore
已經(jīng)配置過(guò)忽略tmp
),并在每個(gè)mytmp
目錄下創(chuàng)建Hello.xml
文件(因?yàn)槿绻麤](méi)有文件,git不會(huì)理會(huì)空目錄的)。
形如:
demo-gitignore
├── child1
│ ├── child2
│ │ ├── child3
│ │ │ └── mytmp
│ │ │ └── Hello.xml
│ │ └── mytmp
│ │ └── Hello.xml
│ └── mytmp
│ └── Hello.xml
└── mytmp
└── Hello.xml
? demo-gitignore git:(master) ? git status -s
?? child1/
?? mytmp/
在.gitignore
中添加/mytmp
忽略后,再看status:
? demo-gitignore git:(master) ? git status -s
M .gitignore
?? child1/
首先發(fā)現(xiàn)?? mytmp/
已經(jīng)不見(jiàn)了(被忽略了)。第一行.gitignore的變化是因?yàn)閯偺砑?code>/mytmp,尚未提交;第二行?? child1/
為什么還在?因?yàn)槲覀冎皇呛雎粤?code>/mytmp目錄,并沒(méi)有忽略其下的文件Hello.xml?其實(shí)是只忽略根目錄下的/mytmp
,子目錄下的/mytmp
并不被忽略。
? demo-gitignore git:(master) ? git add child1
? demo-gitignore git:(master) ? git status -s
M .gitignore
A child1/child2/child3/mytmp/Hello.xml
A child1/child2/mytmp/Hello.xml
A child1/mytmp/Hello.xml
上述唯獨(dú)沒(méi)有提到根目錄demo-gitignore
下的mytmp
目錄。如果要讓所有目錄下的mytmp
目錄都被忽略呢? 前綴加兩個(gè)*
號(hào)(即:**
)。
# project specified gitignore
**/mytmp
此時(shí)mytmp
,都不再顯示,無(wú)論是哪級(jí)子目錄:
? demo-gitignore git:(master) ? git status -s
M .gitignore
如果我們要“排除(不忽略)” /child1/child2/mytmp
目錄呢?
用!/child1/child2/mytmp
排除。
# project specified gitignore
**/mytmp
!/child1/child2/mytmp
結(jié)果驗(yàn)證如下:
? demo-gitignore git:(master) ? git status -s
M .gitignore
A child1/child2/mytmp/Hello.xml
總結(jié)備忘
以
/
開(kāi)頭忽略目錄,表示當(dāng)前。例如/mytmp
表示忽略根目錄下的mytmp。
以**/
開(kāi)頭,忽略所有目錄。例如**/mytmp
表示忽略所有層級(jí)下的mytmp目錄。
用!
開(kāi)頭表示例外。例如!/child1/child2/mytmp
表示單獨(dú)強(qiáng)調(diào)“不忽略”/child1/child2/mytmp的 mytmp 目錄。
忽略的例外
如前文所說(shuō),例外用!
表示。這里補(bǔ)充下關(guān)于“文件”的例外。在上述的實(shí)驗(yàn)環(huán)境中,新創(chuàng)建文件 demo-gitignore/mytmp/HelloExpectional.xml
,并配置.gitignore
如下:
# project specified gitignore
/mytmp/*
!/mytmp/HelloExpectional.xml
它表示忽略根目錄/下的mytmp子目錄下的所有文件(星號(hào)表示),但是/mytmp/HelloExpectional.xml
文件例外(不忽略)。
? demo-gitignore git:(master) ? git add .
? demo-gitignore git:(master) ? git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: .gitignore
new file: child1/child2/child3/mytmp/Hello.xml
new file: child1/child2/mytmp/Hello.xml
new file: child1/mytmp/Hello.xml
new file: mytmp/HelloExpectional.xml
如上結(jié)果,只有mytmp/Hello.xml被忽略。如預(yù)期。如果要所有mytmp呢?用**/tmp
呀。
為什么ignore 沒(méi)生效?
緊接著上面,把.gitignore
內(nèi)容修改為:
# project specified gitignore
**/mytmp/*
!/mytmp/HelloExpectional.xml
查看status,發(fā)現(xiàn)并沒(méi)有變化?
? demo-gitignore git:(master) ? git status -s
MM .gitignore
A child1/child2/child3/mytmp/Hello.xml
A child1/child2/mytmp/Hello.xml
A child1/mytmp/Hello.xml
A mytmp/HelloExpectional.xml
預(yù)期應(yīng)該是只有mytmp/HelloExceptional.xml
不被忽略,其他均被忽略。新配置為什么沒(méi)生效?因?yàn)榍拔?code>git add .的時(shí)候,已經(jīng)加入git索引了,gitignore只能對(duì)untracked
狀態(tài)的資源起作用。
先把他們從tracked (to be committed)
中撤掉:
? demo-gitignore git:(master) ? git rm --cached -r child1
rm 'child1/child2/child3/mytmp/Hello.xml'
rm 'child1/child2/mytmp/Hello.xml'
rm 'child1/mytmp/Hello.xml'
? demo-gitignore git:(master) ? git rm --cached -r mytmp
rm 'mytmp/HelloExpectional.xml'
? demo-gitignore git:(master) ?
命令解釋如下:
git rm --cached
表示直接刪除“索引區(qū)”的內(nèi)容(不是導(dǎo)出到Working dir,也不是提交到版本庫(kù))。后面接文件,表示操作對(duì)象;-r
是當(dāng)操作對(duì)象為目錄時(shí),表示遞歸。
接著實(shí)驗(yàn)看看新的ignore規(guī)則:
? demo-gitignore git:(master) ? git add .
? demo-gitignore git:(master) ? git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: .gitignore
new file: mytmp/HelloExpectional.xml
目錄忽略,它的子目錄和文件呢?
當(dāng)我們忽略一個(gè)目錄時(shí),它下面的子目錄和文件也一起被忽略嗎?
在demo-gitignore/mytmp
創(chuàng)建一級(jí)子目錄son-of-mytmp
和二級(jí)子目錄grandson-of-mytmp
,并各自放一個(gè)文件,如下結(jié)構(gòu):
? demo-gitignore git:(master) ? tree mytmp
mytmp
├── Hello.xml
├── HelloExpectional.xml
└── son-of-mytmp
├── grandson-of-mytmp
│ └── grandson.xml
└── son.xml
2 directories, 4 files
ignore配置:
# project specified gitignore
/mytmp
!/mytmp/HelloExpectional.xml
查看狀態(tài):
? demo-gitignore git:(master) ? git status -s
M .gitignore
?? child1/
的的確確 根目錄下的mytmp目錄及其子目錄,都被忽略了。但與此同時(shí)奇怪的是!/mytmp/HelloExpectional.xml
“例外設(shè)置”并沒(méi)有生效?
如果調(diào)整 ignore 設(shè)置:
# project specified gitignore
/mytmp/*
!/mytmp/HelloExpectional.xml
從/mytmp
調(diào)整為/mytmp/*
,結(jié)果例外生效了。
? demo-gitignore git:(master) ? git add .
? demo-gitignore git:(master) ? git status -s
M .gitignore
A child1/child2/child3/mytmp/Hello.xml
A child1/child2/mytmp/Hello.xml
A child1/mytmp/Hello.xml
A mytmp/HelloExpectional.xml
總結(jié)備忘
忽略目錄
/mytmp
和/mytmp/*
,都會(huì)遞歸影響其子目錄和文件的忽略。
只有/mytmp/*
忽略,才能添加形如!/mytmp/HelloExpectional.xml
的例外。
glob模式語(yǔ)法
所謂“glob模式”就是我們常見(jiàn)的bash下簡(jiǎn)化的正則表達(dá)式。就4招:
- 星號(hào)
*
,通配多個(gè)字符; - 兩個(gè)星號(hào)
**
,表示任意中間層目錄。例如a/**/z
可以匹配目錄a/z
,a/b/z
或a/b/c/z
等。 - 問(wèn)號(hào)
?
,通配單個(gè)字符; - 方號(hào)
[]
,枚舉單個(gè)字符。例如[abc]
表示要么a
,要么b
,要么c
,但是ab
兩個(gè)字符是不能匹配的,只能是1個(gè)。 - 范圍
[0-9]
或[a-z]
表示任意一個(gè)數(shù)字或字母。 - 嘆號(hào)
!
,表示“取反”,表示“不忽略”的語(yǔ)義。
使用習(xí)慣
基本概念
-
.gitignore
文件是項(xiàng)目根目錄下的一個(gè)隱藏文件,不是.git
子目錄下的。 -
.gitignore
文件對(duì)其所在的目錄及其全部子目錄均有效。當(dāng)然用戶(hù)級(jí)HOME目錄下~/.gitignore
文件全局有效,項(xiàng)目的ignore繼承覆蓋用戶(hù)級(jí)的。 - 配置文件
.gitignore
本身需要加入版本庫(kù),以便其他組員能共享同一套資源忽略管理規(guī)則。
? demo-gitignore git:(master) ? touch .gitignore
? demo-gitignore git:(master) ? git status -s
?? .gitignore
? demo-gitignore git:(master) ? git add .gitignore
? demo-gitignore git:(master) ? git commit .gitignore -m 'create project specified gitignore conf'
共享式 與 獨(dú)享式
ignore 規(guī)則既可以選擇“共享式”讓全組員使用同樣的規(guī)則(文件位置是項(xiàng)目根目錄下的.gitignore
文件),好處是大家的配置一樣,不好是.gitignore
內(nèi)容太多,維護(hù)太累。也可以選擇“獨(dú)享式”,只對(duì)自己生效,其他組員看不到(因?yàn)槎疾簧蟼鞯桨姹編?kù))。“獨(dú)享式”有兩種形式:
- 用戶(hù)級(jí)的 位置在
~/.gitignore
用戶(hù)HOME目錄下; - 項(xiàng)目級(jí)的 位置在
.git/info/exclude
,它也是一個(gè)ignore文件,語(yǔ)法規(guī)則是一樣的。注意:盡管.git
目錄一定是要上傳到版本庫(kù)的(它就是版本庫(kù)本身),但是卻留下了exclue
是不上傳的。感覺(jué).git
的設(shè)計(jì)者很有用心。
那我們什么時(shí)候共享式,什么時(shí)候獨(dú)享式呢?個(gè)人覺(jué)得,更多的是團(tuán)隊(duì)的一個(gè)約定。我們可以先對(duì)需要ignore的東西,做個(gè)大致分類(lèi):
-
操作系統(tǒng)層面的 比如Mac OS的
.DS_Store
, windows的Thumbs.db
; -
IDE層面的 比如Eclipse的
.project
,.settings/
和.classpath
。 IDE層面還包括“樸素IDE”,比如臨時(shí)用VIM應(yīng)急修改了個(gè)東西,意外的閃崩生成了一個(gè).swap
文件或有些編輯器會(huì)生成.bak
備份文件。 - 中間結(jié)果類(lèi) 比如程序運(yùn)行一下,就打些日志到文件。再比如嵌入式數(shù)據(jù)庫(kù)生成的臨時(shí)文件。
- 語(yǔ)言相關(guān)的 剛說(shuō)的“中間結(jié)果”日志類(lèi)的是通用的,無(wú)論哪種語(yǔ)言開(kāi)發(fā)的程序都會(huì)輸出日志,除此外,還有喝多跟語(yǔ)言編譯相關(guān)的,比如JAVA的.class字節(jié)碼,比如Web項(xiàng)目構(gòu)建時(shí)生成的.war包。
了解這些后,或許我們可以把前面兩類(lèi)作為“獨(dú)享式”只作用于自己本地,比如你用Mac那你配Mac的ignore,用Eclipse配Eclipse的;別人用Window,他自己配置Windows的。然后把中間結(jié)果和語(yǔ)言相關(guān)的,弄成“共享式”的,在全組員中共享。
這么多配置需要我們自己寫(xiě)嗎? 當(dāng)然不用,這些問(wèn)題很多開(kāi)發(fā)者都是要遇到同樣的問(wèn)題的,把各種環(huán)境窮舉下? 事實(shí)上有人給我們做了。
官方ignore模板
官方提供ignore模板 https://github.com/github/gitignore
它的組織形式就是按上文說(shuō)的“分層組織”。比如:
- 系統(tǒng)層
- Mac的: macOS.gitignore
- Linux的: Linux.gitignore
- IDE層
- Eclipse的: Eclipse.gitignore
- VIM樸素編輯器的: Vim.gitignore
- NetBeans的: NetBeans.gitignore
- 語(yǔ)言層
- JAVA的:Java.gitignore
- GO的: Go.gitignore
- LUA的: Lua.gitignore
- C的:C.gitignore
- C++的: C++
附錄1:如何刪除已經(jīng)提交,但不需要提交的資源?
盡管提倡項(xiàng)目開(kāi)始的時(shí)候,就需要對(duì)資源ignore 規(guī)則進(jìn)行設(shè)置。但是現(xiàn)實(shí)常常沒(méi)有那么理想,往往提交后才發(fā)現(xiàn)提交了一些不應(yīng)該提交的東西。怎么刪除它們?
首先要區(qū)分兩類(lèi)刪除:
真的不需要的,比如每次編譯產(chǎn)生的
.class
,這些文件真的不需要。需要,但不想提交到版本庫(kù)的。比如某些臨時(shí)的document,你打算提交到wiki,而不是版本庫(kù)。
找出“已經(jīng)提交,但不需要提交的”資源
從遠(yuǎn)程拷貝一份。之所以這么做,不用當(dāng)前本地的,是因?yàn)閕gnore規(guī)則的存在,本地一定與遠(yuǎn)程不是完全一致的(從文件系統(tǒng)的角度說(shuō)的完全一致,不是git diff角度)。
git clone http://10.77.144.192:11824/blueocean/passport.git
然后,比如假設(shè)我們之前誤提交了.class
文件,那么需要找出:
find . -name "*.class"
發(fā)現(xiàn)./WebRoot/WEB-INF/classes/
下面居然有,刪除它們。同時(shí)在新拷貝的和本地現(xiàn)有的都刪除。