建议阅读官方文档:https://git-scm.com/book/zh/v2
一、git 是什么
简单的说 git 是一个分布式版本控制系统,那么什么是分布式版本控制系统?在了解它之前,我们先来了解下本地版本控制系统和集中化版本控制系统。
本地版本控制系统:许多人习惯通过复制整个项目目录的方式来保存不同的版本。这么做唯一的好处就是简单,但是特别容易犯错。有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想之外的文件。为了解决这个问题,就有了本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
集中化版本控制系统:为了让在不同系统上的开发者协同工作,集中化的版本控制系统应运而生。这类系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的开发者都通过客户端连到这台服务器,取出最新的文件或者提交更新。
分布式版本控制系统:分布式相比于集中化的最大区别是开发者可以提交到本地做版本控制,并且客户端也并不只提取最新版本的文件快照,而是把代码仓库完整地 clone 下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个 clone 出来的本地仓库恢复,因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
二、git 工作流程
Git 有三种状态,你的文件可能处于其中之一: 已修改(modified)、已暂存(staged)和已提交(committed)。
• 已修改:表示修改了文件,但还没保存到版本库中。
• 已暂存:表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中(git以快照方式保存数据)。
• 已提交:表示数据已经安全地保存在版本库中。
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区和版本库。
• 工作区:或者说工作目录,里面放着项目的内容。
• 暂存区:是一个文件,保存了下次将要提交的文件列表信息,一般存放在 .git 目录下的 index 文件中。
• 版本库:用来保存项目的元数据和对象数据库的地方(即.git目录)。这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
基本的 Git 工作流程如下:
• 在工作区中修改文件。
• 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
• 提交更新,找到暂存区的文件,将快照永久性存储到版本库中。
如果文件自上次检出后,作了修改但还没有放到暂存区域,就是已修改状态。如果文件已修改并放入暂存区,就属于已暂存状态。 如果版本库中保存着特定版本的文件,就属于已提交状态。
三、git 基本操作
基本命令:
命令 | 含义 |
---|---|
git init | 创建一个空的git仓库或重新初始化一个现有的仓库 |
git add | 将文件内容添加到暂存区 |
git rm | 从工作区和暂存区中删除文件 |
git commit | 将暂存区的文件提交到版本库中 |
git log | 查看提交的历史记录 |
git reflog | 查看所有历史记录 |
git status | 检查当前文件状态 |
git reset | 回退到指定的版本 |
安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中。然后使用 git init 创建一个新的 git 仓库,该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。
[root@git ~]# yum install -y git
[root@git ~]# git config --global user.name "崔鹏"
[root@git ~]# git config --global user.email "cuipeng_work@163.com"
[root@git ~]# mkdir /test && cd /test
[root@git test]# git init # 创建一个git仓库
Initialized empty Git repository in /test/.git/
[root@git test]# ll -a # 此时我们可以看到目录下生成了.git目录
total 0
drwxr-xr-x 3 root root 18 Dec 22 20:06 .
dr-xr-xr-x. 18 root root 236 Dec 22 20:05 ..
drwxr-xr-x 7 root root 119 Dec 22 20:06 .git
[root@git test]# ls -F1 .git/ # 目录结构
branches/ # 分支相关
config # 包含项目特有的配置选项
description # 仅供 GitWeb 程序使用,无需关心
HEAD # 指向目前被检出的分支
hooks/ # 包含客户端或服务端的钩子脚本
info/ # 包含一个全局性排除(global exclude)文件,用以放置那些不希望被记录在.gitignore 文件中的忽略模式
objects/ # 存储所有数据内容
refs/ # 存储指向数据(分支、远程仓库和标签等)的提交对象的指针
index(尚未创建) # 保存暂存区信息
以下我们创建一个新文件,可以用 git status 命令查看哪些文件处于什么状态,我们会看到一个新的未跟踪文件。使用命令 git add 开始跟踪一个文件,此时再运行 git status 命令,会看到 index.html 文件已被跟踪,并处于暂存状态。只要文件出现 Changes to be committed 这行下面,就说明文件已是暂存状态。如果此时提交,那么该文件在你运行 git add 时的版本将被留存在后续的历史记录中。每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
[root@git test]# echo "first test" > index.html
[root@git test]# git status # 检查当前文件状态,显示文件未被跟踪管理
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# index.html
nothing added to commit but untracked files present (use "git add" to track)
[root@git test]# git add . # 跟踪文件,将文件添加到暂存区,“.”表示当前目录所有文件
[root@git test]# git status # 此时会看到 index.html 文件已被跟踪,并处于暂存状态
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: index.html
#
[root@git test]# git commit -m "第一次测试" # 提交暂存区文件到版本库,-m选项指定提交描述信息
[master (root-commit) 6ac7238] 第一次测试
1 file changed, 1 insertion(+)
create mode 100644 index.html
[root@git test]# git status
# On branch master
nothing to commit, working directory clean
[root@git test]# git log # 查看提交的历史记录
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
修改文件再次提交。
[root@git test]# echo "second test" >> index.html
[root@git test]# git status # 此时文件为已修改状态
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: index.html
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git test]# git commit -m "第二次测试"
[master 417e3a1] 第二次测试
1 file changed, 1 insertion(+)
[root@git test]# git log
commit 417e3a14992c46ca9294b45f7102b4391c6d3aec
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以使用 git commit –amend 来重新提交,这个命令会将暂存区中的文件提交。如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。
[root@git test]# git commit --amend -m "第二次测试v1"
[master d1a899c] 第二次测试v1
1 file changed, 1 insertion(+)
[root@git test]# git log
commit d1a899cc7c8ff632179cb797af008f9f255f0f4c
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试v1
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
[root@git test]# git commit --amend -m "第二次测试"
[master a1b2f2a] 第二次测试
1 file changed, 1 insertion(+)
回滚操作,我们可以使用 git reset –hard commitID 来进行回滚操作。
[root@git test]# git log
commit a1b2f2a0abe541467c3be74d9141dfbc86568ee2
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
[root@git test]# git reset --hard 6ac7238
HEAD is now at 6ac7238 第一次测试
[root@git test]# git log
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
[root@git test]# cat index.html
first test
# 但是如果我们又想回到第二次测试版本,但是此时 git log 只显示第一次测试及它之前的版本。
# 此时我们可以使用 git reflog 来查看所有的历史记录信息,或者使用git log --all
[root@git test]# git reflog
6ac7238 HEAD@{0}: reset: moving to 6ac7238
a1b2f2a HEAD@{1}: commit (amend): 第二次测试
d1a899c HEAD@{2}: commit (amend): 第二次测试v1
417e3a1 HEAD@{3}: commit: 第二次测试
6ac7238 HEAD@{4}: commit (initial): 第一次测试
[root@git test]# git reset --hard a1b2f2a
HEAD is now at a1b2f2a 第二次测试
[root@git test]# cat index.html
first test
second test
四、远程仓库
相关命令:
命令 | 含义 |
---|---|
git remote | 远程仓库操作 |
git clone | 克隆仓库,一般用于远程拷贝仓库 |
git push | 上传代码到远程仓库并合并 |
git pull | 下载远程仓库的最新代码并合并(merge)到当前分支 |
git fetch | 下载远程仓库的最新代码,但是不会自动(merge) |
远程仓库是指托管在因特网或其他网络中的你的项目的版本库。你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。例如常见的代码托管平台有github、gitee以及自建的gitlab,我们可以将自己的项目的版本库托管到这些平台上,这里我们已 gitee 为例(当前其他平台操作大多类似)。
在 gitee 上创建一个仓库,创建完后,此时我们可以看到以下提示信息。
我们根据信息操作:首先使用 “git remote add
[root@git test]# git remote add origin https://gitee.com/cui-peng/test.git
[root@git test]# git remote -v # 查看已经配置的远程仓库服务器
origin https://gitee.com/cui-peng/test.git (fetch)
origin https://gitee.com/cui-peng/test.git (push)
[root@git test]# git push -u origin master
此时我们在到gitee 提示页面刷新就可以看到我们上传的内容了:
假如我们下班回家,在家想写写代码,就可以直接从远程仓库将我们的项目克隆到本地Windows电脑(需要安装git)。
asus@LAPTOP-1AK4IIRE MINGW64 ~ (master)
$ git clone https://gitee.com/cui-peng/test.git
Cloning into 'test'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), 473 bytes | 20.00 KiB/s, done.
asus@LAPTOP-1AK4IIRE MINGW64 ~ (master)
$ cd test/
asus@LAPTOP-1AK4IIRE MINGW64 ~/test (master)
$ cat index.html
first test
second test
五、标签
Git 可以给仓库历史中的某一个提交打上标签,例如我们通常会使用这个功能来标记发布节点( 如v1.0 、v2.0 等等)。
创建标签,默认对最新提交打标签。
[root@git test]# git tag -a "v2" -m "版本v2"
列出标签:
[root@git test]# git tag
v2
查看标签的具体信息:
[root@git test]# git show v2
tag v2
Tagger: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 23:14:56 2020 -0500
版本v2
commit a1b2f2a0abe541467c3be74d9141dfbc86568ee2
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
......
对之前的提交打标签:
[root@git test]# git tag -a "v1" -m "版本v1" 6ac7238
[root@git test]# git tag
v1
v2
[root@git test]# git show v1
tag v1
Tagger: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 23:20:34 2020 -0500
版本v1
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
......
推送标签,默认情况下,git push 命令并不会传送标签到远程仓库,在创建完标签后必须显式地推送标签到远程仓库上。当前在 git push 时也可以使用 –tags 选项,这将会把所有不在远程仓库服务器上的标签全部push上去。
[root@git test]# git push origin v1 v2
Username for 'https://gitee.com': 18229282903
Password for 'https://18229282903@gitee.com':
Counting objects: 2, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 306 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-5.0]
To https://gitee.com/cui-peng/test.git
* [new tag] v1 -> v1
* [new tag] v2 -> v2
删除标签:
[root@git test]# git tag -d v1 v2
Deleted tag 'v1' (was abfe907)
Deleted tag 'v2' (was 5dfd009)
[root@git test]# tag
-bash: tag: command not found
[root@git test]# git tag
# 但是上述命令并不会从任何远程仓库中移除这个标签,如果需要删除,有两种方法:
(1)Usage:git push <remote> :refs/tags/<tagname>
[root@git test]# git push origin :refs/tags/v1
remote: Powered by GITEE.COM [GNK-5.0]
To https://gitee.com/cui-peng/test.git
- [deleted] v1
(2)Usage:git push origin --delete <tagname>
[root@git test]# git push origin --delete v2
remote: Powered by GITEE.COM [GNK-5.0]
To https://gitee.com/cui-peng/test.git
- [deleted] v2
六、分支
相关命令:
命令 | 含义 |
---|---|
git branch | 列出、创建或删除分支 |
git checkout | 切换分支 |
git merge | 合并分支 |
git pull | 下载远程仓库的最新代码并合并(merge)到当前分支 |
git fetch | 下载远程仓库的最新代码,但是不会自动合并(merge) |
几乎所有的版本控制系统都以某种形式支持分支,git也不例外,而且Git 处理分支的方式是非常轻量的,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。
下面我们将通过一个示例来了解分支的相关操作。
[root@git test]# git branch bug # 创建一个名为bug的分支,这会在当前所在的提交对象上创建一个指针,即分支基于当前最新的提交版本
[root@git test]# git branch # 列出分支
bug
* master
[root@git test]# git checkout bug # 切换到bug分支
Switched to branch 'bug'
[root@git test]# git branch
* bug
master
上面我们创建了一个bug分支来进行bug修复,下面我们将修复好bug,并将bug分支合并到master上。
[root@git test]# vim index.html # bug修复
first test bug修复
second test
[root@git test]# git add .
[root@git test]# git commit -m "v1 bug修改"
[bug 0c470c6] v1 bug修改
1 file changed, 1 insertion(+), 1 deletion(-)
[root@git test]# git log
commit 0c470c66460e57813e61e0cf32617262b87e6451
Author: 崔鹏 <cuipeng_work@163.com>
Date: Wed Dec 23 02:23:23 2020 -0500
v1 bug修改
commit a1b2f2a0abe541467c3be74d9141dfbc86568ee2
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
[root@git test]# git checkout master
Switched to branch 'master'
[root@git test]# git log # 切换回主分支,并没有bug修复的那个版本
commit a1b2f2a0abe541467c3be74d9141dfbc86568ee2
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
[root@git test]# git merge bug # 合并bug分支到主分支
Updating a1b2f2a..0c470c6
Fast-forward
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
[root@git test]# git log
commit 0c470c66460e57813e61e0cf32617262b87e6451
Author: 崔鹏 <cuipeng_work@163.com>
Date: Wed Dec 23 02:23:23 2020 -0500
v1 bug修改
commit a1b2f2a0abe541467c3be74d9141dfbc86568ee2
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:30:20 2020 -0500
第二次测试
commit 6ac72385919e4a2e8b93d669320b5857c483f767
Author: 崔鹏 <cuipeng_work@163.com>
Date: Tue Dec 22 22:27:36 2020 -0500
第一次测试
分支管理:
1、查看每一个分支的最后一次提交
[root@git test]# git branch -v
bug 0c470c6 v1 bug修改
* master 0c470c6 [ahead 1] v1 bug修改
2、查看分支是否合并
--merged 与 --no-merged 这两个选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支
[root@git test]# git branch --merged # 查看哪些分支已经合并到当前分支
bug
* master
[root@git test]# git branch --no-merged # 查看未合并的分支,当前为无
[root@git test]# git branch test
[root@git test]# git branch --no-merged
[root@git test]# git branch --merged
bug
* master
test
# 上面我们创建了一个test分支,发现test包含在以及合并的分支列表内。这是因为test分支当前没有任何提交,和主分支是一模一样的。
# 所以包含在在内,切换到test分支进行一些修改提交,如何在使用上述命令就会有我们预期的结果,这里就不演示了。
3、删除分支
# -d:删除完全合并的分支。-D:删强制删除分支(即使未合并)
[root@git test]# git branch -d bug
Deleted branch bug (was 0c470c6).
[root@git test]# git branch -D test
Deleted branch test (was 0c470c6).
[root@git test]# git branch
* master
远程分支:
[root@git test]# git branch bug
[root@git test]# git checkout bug
Switched to branch 'bug'
[root@git test]# vim index.html
first test bug修复
second test bug修复
[root@git test]# git add .
[root@git test]# git commit -m "v2 bug修复"
[bug ca38015] v2 bug修复
1 file changed, 1 insertion(+), 1 deletion(-)
[root@git test]# git push origin bug # 推送bug分支
Counting objects: 8, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 540 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-5.0]
remote: Create a pull request for 'bug' on Gitee by visiting:
remote: https://gitee.com/cui-peng/test/pull/new/cui-peng:bug...cui-peng:master
To https://gitee.com/cui-peng/test.git
* [new branch] bug -> bug
# 我们假设在另一台电脑上,拉取bug分支。
[root@git test]# git branch -D bug
Deleted branch bug (was ca38015).
# 此时有两种方法:
(1)git fetch
[root@git test]# git fetch origin bug # 只下载远程仓库的bug分支的最新代码,但是不会自动合并(merge)
From https://gitee.com/cui-peng/test
* branch bug -> FETCH_HEAD
[root@git test]# cat index.html
first test bug修复
second test
[root@git test]# git branch -av # 查看分支和远程分支
* master 0c470c6 [ahead 1] v1 bug修改
remotes/origin/bug ca38015 v2 bug修复
remotes/origin/master a1b2f2a 第二次测试
(2)git pull
[root@git test]# git pull origin bug # 下载远程仓库的bug分支最新代码并合并(merge)到当前分支
From https://gitee.com/cui-peng/test
* branch bug -> FETCH_HEAD
Updating 0c470c6..ca38015
Fast-forward
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
[root@git test]# cat index.html
first test bug修复
second test bug修复
特别注意:有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们,就会产生冲突,此时我们就需要手动去修改解决它非常麻烦。
七、其它
1、免密
各大代码托管平台都支持SSH,也就是使用密钥,而无需频繁输出用户名和密码。具体流程:
[root@git .ssh]# ssh-keygen # 生成密钥
# 复制公钥,找到代码托管平台设置SSH公钥处进行添加。然后将我们的远程仓库地址从http换到SSH即可。
[root@git test]# git remote remove origin
[root@git test]# git remote add origin git@gitee.com:cui-peng/test.git
[root@git test]# git remote -v
origin git@gitee.com:cui-peng/test.git (fetch)
origin git@gitee.com:cui-peng/test.git (push)
[root@git test]# git push origin master
The authenticity of host 'gitee.com (212.64.62.183)' can't be established.
ECDSA key fingerprint is SHA256:FQGC9Kn/eye1W8icdBgrQp+KkGYoFgbVr17bmjey0Wc.
ECDSA key fingerprint is MD5:27:e5:d3:f7:2a:9e:eb:6c:93:cd:1f:c1:47:a3:54:b1.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitee.com,212.64.62.183' (ECDSA) to the list of known hosts.
Total 0 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-5.0]
To git@gitee.com:cui-peng/test.git
a1b2f2a..ca38015 master -> master
2、忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式。
来看一个实际的 .gitignore 例子:
[root@git test]# vim .gitignore
*.[oa]
*~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有名字以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 log、tmp 或者 pid 目录,以及自动生成的文档等等。要养成一开始就为你的新仓库设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore 的格式规范如下:
• 所有空行或者以 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
• 匹配模式可以以(/)开头防止递归。
• 匹配模式可以以(/)结尾指定目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式:
• *:匹配零个或多个任意字符。
• **:表示匹配任意中间目录,比如 a/**/z 可以匹配 a/z 、 a/b/z 或 a/b/c/z 等。
• [abc]:匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c)。
• [0-9]:表示匹配所有 0 到 9 的数字。在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配
• ?:只匹配一个任意字符。
我们再看一个 .gitignore 文件的例子:
# 忽略所有的 .a 文件
*.a
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf