手把手带你玩git之gitignore

内容提要

  • 忽略文件
  • 忽略目录的四种不同方式
  • /mytmp
  • /mytmp/*
  • **/mytmp
  • **/mytmp/*
  • 例外
  • ! 表示例外;
  • 作用于/mytmp/*,不能作用于/mytmp
  • ignore 生效的前提
  • 通配符语法
  • 一个星表示任意字符;
  • 两个星表示任意路径
  • 两种形式:共享式和独享式
  • 官方模板

语法

当我们进行代码开发时,把代码存到远程代码库上。但是总有一些代码是不需要上传的,比如编译的中间文件,单元测试自动生成的测试报告等。这个时候我们需要告诉git,哪些文件应该忽略,git提供了这种机制,它通过.gitignore 配置文件来实现。通常该文件放在项目的根目录下,我们可以手动创建它,然后编辑内容。

忽略文件

.gitignore文件中编辑:

#.gitignore for java
*.class

第一行以#开头的是注释,*.class 表示忽略“所有”以.class为后缀的文件(其中*号表示glob模式匹配的通配符)。这里的“所有”无论它在哪个目录下。

实验验证下,创建多级子目录,每个目录创建一个.class文件,结构如下:

➜  demo-gitignore git:(master) tree
.
├── L1.class
└── child1
    ├── L2.class
    └── child2
        ├── L3.class
        └── child3
            └── L4.class

3 directories, 4 files

执行git status,看看有没有被忽略?

➜  demo-gitignore git:(master) git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)

当然也可以不用通配符,例如

# project specified gitignore
Hello.xml

表示忽略“所有”名字叫Hello.xml的文件。

忽略目录

语法上,以/开头的表示忽略目录。比如/mytmp表示忽略“根目录下”名叫mytmp的目录,并非表示“所有”。

在上述3个childX目录下,各自创建一个mytmp子目录(实验时请勿用tmp,以免用户目录下的~/.gitignore已经配置过忽略tmp),并在每个mytmp目录下创建Hello.xml文件(因为如果没有文件,git不会理会空目录的)。

形如:

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/

首先发现?? mytmp/已经不见了(被忽略了)。第一行.gitignore的变化是因为刚添加/mytmp,尚未提交;第二行?? child1/为什么还在?因为我们只是忽略了/mytmp目录,并没有忽略其下的文件Hello.xml?其实是只忽略根目录下的/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

上述唯独没有提到根目录demo-gitignore下的mytmp目录。如果要让所有目录下的mytmp目录都被忽略呢? 前缀加两个*号(即:**)。

# project specified gitignore
**/mytmp

此时mytmp,都不再显示,无论是哪级子目录:

➜  demo-gitignore git:(master) ✗ git status -s
 M .gitignore

如果我们要“排除(不忽略)” /child1/child2/mytmp 目录呢?
!/child1/child2/mytmp排除。

# project specified gitignore
**/mytmp
!/child1/child2/mytmp

结果验证如下:

➜  demo-gitignore git:(master) ✗ git status -s
 M .gitignore
A  child1/child2/mytmp/Hello.xml

总结备忘

/开头忽略目录,表示当前。例如/mytmp表示忽略根目录下的mytmp。
**/开头,忽略所有目录。例如**/mytmp表示忽略所有层级下的mytmp目录。
!开头表示例外。例如!/child1/child2/mytmp表示单独强调“不忽略”/child1/child2/mytmp的 mytmp 目录。

忽略的例外

如前文所说,例外用!表示。这里补充下关于“文件”的例外。在上述的实验环境中,新创建文件 demo-gitignore/mytmp/HelloExpectional.xml,并配置.gitignore如下:

# project specified gitignore
/mytmp/*
!/mytmp/HelloExpectional.xml

它表示忽略根目录/下的mytmp子目录下的所有文件(星号表示),但是/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

如上结果,只有mytmp/Hello.xml被忽略。如预期。如果要所有mytmp呢?用**/tmp呀。

为什么ignore 没生效?

紧接着上面,把.gitignore内容修改为:

# project specified gitignore
**/mytmp/*
!/mytmp/HelloExpectional.xml

查看status,发现并没有变化?

➜  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

预期应该是只有mytmp/HelloExceptional.xml不被忽略,其他均被忽略。新配置为什么没生效?因为前文git add .的时候,已经加入git索引了,gitignore只能对untracked状态的资源起作用。

先把他们从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 表示直接删除“索引区”的内容(不是导出到Working dir,也不是提交到版本库)。后面接文件,表示操作对象;-r是当操作对象为目录时,表示递归。

接着实验看看新的ignore规则:

➜  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

目录忽略,它的子目录和文件呢?

当我们忽略一个目录时,它下面的子目录和文件也一起被忽略吗?

demo-gitignore/mytmp创建一级子目录son-of-mytmp和二级子目录grandson-of-mytmp,并各自放一个文件,如下结构:

➜  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

查看状态:

➜  demo-gitignore git:(master) ✗ git status -s
 M .gitignore
?? child1/

的的确确 根目录下的mytmp目录及其子目录,都被忽略了。但与此同时奇怪的是!/mytmp/HelloExpectional.xml“例外设置”并没有生效?

如果调整 ignore 设置:

# project specified gitignore
/mytmp/*
!/mytmp/HelloExpectional.xml

/mytmp调整为/mytmp/*,结果例外生效了。

➜  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

总结备忘

忽略目录/mytmp/mytmp/*,都会递归影响其子目录和文件的忽略。
只有/mytmp/*忽略,才能添加形如!/mytmp/HelloExpectional.xml的例外。

glob模式语法

所谓“glob模式”就是我们常见的bash下简化的正则表达式。就4招:

  • 星号 *,通配多个字符;
  • 两个星号**,表示任意中间层目录。例如a/**/z 可以匹配目录a/z, a/b/za/b/c/z等。
  • 问号?,通配单个字符;
  • 方号[],枚举单个字符。例如[abc]表示要么a,要么b,要么c,但是ab两个字符是不能匹配的,只能是1个。
  • 范围[0-9][a-z] 表示任意一个数字或字母。
  • 叹号!,表示“取反”,表示“不忽略”的语义。

使用习惯

基本概念

  • .gitignore 文件是项目根目录下的一个隐藏文件,不是.git子目录下的。
  • .gitignore 文件对其所在的目录及其全部子目录均有效。当然用户级HOME目录下~/.gitignore文件全局有效,项目的ignore继承覆盖用户级的。
  • 配置文件.gitignore本身需要加入版本库,以便其他组员能共享同一套资源忽略管理规则。
➜  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'

共享式 与 独享式

ignore 规则既可以选择“共享式”让全组员使用同样的规则(文件位置是项目根目录下的.gitignore文件),好处是大家的配置一样,不好是.gitignore内容太多,维护太累。也可以选择“独享式”,只对自己生效,其他组员看不到(因为都不上传到版本库)。“独享式”有两种形式:

  • 用户级的 位置在~/.gitignore 用户HOME目录下;
  • 项目级的 位置在.git/info/exclude,它也是一个ignore文件,语法规则是一样的。注意:尽管.git目录一定是要上传到版本库的(它就是版本库本身),但是却留下了exclue是不上传的。感觉.git的设计者很有用心。

那我们什么时候共享式,什么时候独享式呢?个人觉得,更多的是团队的一个约定。我们可以先对需要ignore的东西,做个大致分类:

  • 操作系统层面的 比如Mac OS的 .DS_Store, windows的Thumbs.db
  • IDE层面的 比如Eclipse的.project, .settings/.classpath。 IDE层面还包括“朴素IDE”,比如临时用VIM应急修改了个东西,意外的闪崩生成了一个.swap文件或有些编辑器会生成.bak备份文件。
  • 中间结果类 比如程序运行一下,就打些日志到文件。再比如嵌入式数据库生成的临时文件。
  • 语言相关的 刚说的“中间结果”日志类的是通用的,无论哪种语言开发的程序都会输出日志,除此外,还有喝多跟语言编译相关的,比如JAVA的.class字节码,比如Web项目构建时生成的.war包。

了解这些后,或许我们可以把前面两类作为“独享式”只作用于自己本地,比如你用Mac那你配Mac的ignore,用Eclipse配Eclipse的;别人用Window,他自己配置Windows的。然后把中间结果和语言相关的,弄成“共享式”的,在全组员中共享。

这么多配置需要我们自己写吗? 当然不用,这些问题很多开发者都是要遇到同样的问题的,把各种环境穷举下? 事实上有人给我们做了。

官方ignore模板

官方提供ignore模板 https://github.com/github/gitignore

它的组织形式就是按上文说的“分层组织”。比如:

  • 系统层
  • IDE层
  • 语言层

附录1:如何删除已经提交,但不需要提交的资源?

尽管提倡项目开始的时候,就需要对资源ignore 规则进行设置。但是现实常常没有那么理想,往往提交后才发现提交了一些不应该提交的东西。怎么删除它们?

首先要区分两类删除:

  • 真的不需要的,比如每次编译产生的 .class,这些文件真的不需要。

  • 需要,但不想提交到版本库的。比如某些临时的document,你打算提交到wiki,而不是版本库。

  • 找出“已经提交,但不需要提交的”资源

从远程拷贝一份。之所以这么做,不用当前本地的,是因为ignore规则的存在,本地一定与远程不是完全一致的(从文件系统的角度说的完全一致,不是git diff角度)。

git clone  http://10.77.144.192:11824/blueocean/passport.git

然后,比如假设我们之前误提交了.class文件,那么需要找出:

find . -name "*.class"

发现./WebRoot/WEB-INF/classes/ 下面居然有,删除它们。同时在新拷贝的和本地现有的都删除。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 本文为 Git教程的学习笔记,教程源自廖雪峰的博客。这是一个由浅入深,学完后能立刻上手的Git教程。另,附上另一本...
    七弦桐语阅读 6,231评论 5 47
  • 1. 安装 Github 查看是否安装git: $ git config --global user.name "...
    Albert_Sun阅读 13,626评论 9 163
  • November012013-11-01 20:47:02|分类:大学日记|字号订阅 今天院里迎新晚会,刚开始...
    独行向日葵阅读 271评论 0 1
  • 不做人, 来到人世干什么。 不做自己, 做人又是为什么。 不投入, 不能成功。 不遁出, 一切没用。 不做自己, ...
    再凑热闹阅读 139评论 0 0