收藏问题Git使用CocoaPods

手把手带你玩git之gitignore

2016-12-07  本文已影响340人  区影

内容提要

语法

当我们进行代码开发时,把代码存到远程代码库上。但是总有一些代码是不需要上传的,比如编译的中间文件,单元测试自动生成的测试报告等。这个时候我们需要告诉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招:

使用习惯

基本概念

➜  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内容太多,维护太累。也可以选择“独享式”,只对自己生效,其他组员看不到(因为都不上传到版本库)。“独享式”有两种形式:

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

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

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

官方ignore模板

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

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

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

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

首先要区分两类删除:

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

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

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

find . -name "*.class"

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

上一篇 下一篇

猜你喜欢

热点阅读