上一篇文章,我们学习了 使用 git 如何创建一个仓库。而且还详细的介绍了 git 各个工作目录的作用。

git : global information tracker

git 其实是内容寻址文件系统。本质是一个 key-value 的数据库。在上一篇文章了解了 git 的四种对象:提交对象,数据对象,树对象和标签对象。还学习了 git 的引用 ( HEAD , Index , refs 目录),以及最后学习了 git 的压缩,( git gc ).

今天我们从 简单的 git 使用流程说起。

上一篇中,我们创建了一个仓库.

1
2
3
➜  gitlearn git init .
Initialized empty Git repository in /private/tmp/gitlearn/.git/
➜ gitlearn git:(master)

新建完仓库之后,我们一般会对仓库进行配置。

# git config

git 自带了一个 git config 工具来帮助设置控制 Git 外观和行为的配置变量。这些变量存储在三个不同的位置。

  • A : /etc/gitconfig 文件:包含系统上每一个用户以及他们仓库的通用配置。 如果执行 git config 时,带上 --system 选项,就会读写该配置文件中的配置属性。
  • B : ~/.gitconfig~/.config/git/config 文件:只针对当前用户。使用 --global 选项会让 git 读写此文件,这会对你系统的上的所有仓库生效。
  • C : 当前使用仓库的 git 目录的 config 文件 ( .git/config ): 只针对当前仓库。 可以使用 --local 选项让 git 强制读写此文件,默认情况下也会使用它。

读取顺序为: A -> B -> C , C 会覆盖 B 中相同的配置, B 会覆盖 A 中相同的配置。

可以通过 git config --list --show origin 命令查查文件位置以及命令所在的文件。

一般我们新建了仓库,或者 clone 一个远程仓库到本地之后,第一件事就是配置自己的用户名和邮件地址。

刚才说过,三种配置方式的作用范围,这里一般情况下会使用 --global 。 当前登录的用户都会使用一样的配置。

1
2
$ git config --global user.name "fangjiaxiaobai"
$ git config --global user.email "fangjiaxiaobai@163.com"

这一点非常重要!

我们来查看一下配置的结果。

1
2
3
$ git config --global --list
user.name=fangjiaxiaobai
user.email=fangjiaxiaobai@163.com

# 修改 git 的默认文本编辑器

git 的文本编辑器有什么作用呢?

当我们的文件 在 提交代码,拉取远程代码或者切换分支出现冲突的时候, git 命令行窗口就会有一个处理冲突之后填写 commit 信息的命令行窗口,这里使用的编辑器就是 我们要说的 “文本编辑器了”。

git 中默认的文本编辑器是 nano 编辑器,我们使用进行配置,使用 vim 编辑器。

1
2
3
4
5
➜  gitlearn git:(master) ✗ git config --global core.editor "vim"
➜ gitlearn git:(master) ✗ git config --global -l
user.name=fangjiaxiaobai
user.email=fangjiaxiaobai@163.com
core.editor=vim

配置完仓库,之后,我们就正式开始使用 git 了。

# 使用 git 进行工作

简单来讲,我们只需三步,两个命令就可以完成文件的版本控制。

  • 修改文件 (创建文件,修改文件内容,删除文件)
  • 使用 git add . 将文件纳入版本管理
  • 使用 git commit -m "xxx" 将文件提交到版本仓库。

在我们的工作目录中 (比如 gitlearn/ ), 所有的文件只有两种状态: 已跟踪未跟踪

已跟踪的文件是指被纳入版本控制的文件。在上一次快照中有他们的记录,在工作一段时间后,他们的状态可能是未修改,已修改,或者已经放入了 暂存区 $^❶ $。

未跟踪的文件:就是 即不存在上次快照中,也没有被放入暂存区中。

这里我们再次的介绍一下 git 的工作区域:

工作区,暂存区,版本库。

工作区,就是我们修改的文件。包括:新建文件,修改文件内容,删除文件。
暂存区,就是 git 将我们修改的文件记录起来起来。
版本库,就是 git 的记录的版本变更历史。就是 .git 目录下的 objects/ 等目录下的所有文件。即我们 进行了 commit 操作之后就会进入了版本库。

#git 跟踪我们的文件

使用 git add 命令,可以让 git 开始跟踪一个文件。

使 git 跟踪文件 README.md

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  gitlearn git:(master) echo 'test git跟踪文件' > README.md
# 查看工作区中文件的状态
➜ gitlearn git:(master) ✗ git status
On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)

README.md

nothing added to commit but untracked files present (use "git add" to track)

这里我们看到了,未跟踪的该文件: README.md 。我们可以使用 git add 命令,将未跟踪的文件纳入跟踪范围。

1
2
3
4
5
6
7
8
9
10
11
➜  gitlearn git:(master) ✗ git add README.md
# 查看文件的状态
➜ gitlearn git:(master) ✗ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: README.md

Changes to be committed : 将要提交的变更。说明下面的文件已经是暂存状态了。使用 git rm --cached <file>... 命令可以取消暂存。此时 README.md 文件已经是 git 跟踪的了。我们再次修改一下 README.md 文件。查看一下工作区中文件的状态.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  gitlearn git:(master) ✗ echo 'test 再次修改已经被跟踪的文件' >> README.md
➜ gitlearn git:(master) ✗ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: README.md

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: README.md

又看到了 Changes not staged for commit : 还没有被暂存的提交去提交。说明第二次的变更还没有被 git 跟踪起来。这就需要我们在运行一次 git add README.md 命令了。

1
2
3
4
5
6
7
8
9
10
➜  gitlearn git:(master) ✗ git add README.md
➜ gitlearn git:(master) ✗ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: README.md

我们可以看到,第二次更改也被 git 跟踪了起来。

git status 命令的输出十分详细,可以使用 git status -s(--short) 命令,输出非常简洁的结果.

1
2
➜  gitlearn git:(master) ✗ git status -s
A README.md

输出结果的格式是这样的:

1
2
XX file1
XX file2

左列 XX: 第一个 X 表示,暂存区的状态,第二个 X 表示工作区的状态。

XX 的规则如下:

  • 新添加的未跟踪文件前面有??标记
  • 新添加到暂存区中的文件前面有 A 标记
  • 修改过的文件前面有 M 标记。

# 提交到版本库

现在我们就可以提交文件了。最好在每次提交之前都用 git status 看下,我们需要的文件是否都放入 暂存区了。然后再用 git commit 进行提交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜  gitlearn git:(master) ✗ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: README.md
# 提交代码

➜ gitlearn git:(master) ✗ git commit -m "first commit"
[master (root-commit) fb554b5] first commit
1 file changed, 2 insertions(+)
create mode 100644 README.md

可以看到,提交后它会告诉你,当前是在哪个分支( master )提交的,本次提交的完整 SHA-1 校验和是什么( 463dc4f ),以及在本次提交中,有多少文件修订过,多少行添加和删改过。

以上就是我们最简单的开发流程了。

这时,如果我不想某个文件纳入版本管理,但是这个文件还必须要在工作区中呢?

# 忽略文件

在这种情况下,我们可以创建一个名为: .gitignore 的文件,列出要忽略的文件的模式。

.gitignore 文件的格式规范如下:

  • 所有空行或者以 # 开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
  • 匹配模式可以以(/)开头防止递归。
  • 匹配模式可以以(/)结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号 (*) 匹配零个或多个任意字符; [abc] 匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a ,要么匹配一个 b ,要么匹配一个 c ); 问号 (?) 只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 09 的数字)。 使用两个星号( ** )表示匹配任意中间目录,比如 a/**/z 可以匹配 a/za/b/za/b/c/z 等。

来看一个 .gitignore 文件的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 忽略所有的 .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

假设:我们还有一个名叫: git.log 的文件。在更早的一个快照中,已经把它纳入了版本库中。现在我们要把它移除掉。应该怎么办呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
### 准备工作
➜ gitlearn git:(master) echo 'test remove logs files' > git.log
➜ gitlearn git:(master) ✗ git add .
➜ gitlearn git:(master) ✗ git commit -m "test remove logs files"
[master b83b8f1] test remove logs files
1 file changed, 1 insertion(+)
create mode 100644 git.log

### 移除 git.log
➜ gitlearn git:(master) git rm git.log
rm 'git.log'
➜ gitlearn git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

deleted: git.log
# 新建 .gitignore 文件
➜ gitlearn git:(master) ✗ echo '*.log' > .gitignore
➜ gitlearn git:(master) ✗ git add .
# 查看文件的状态
➜ gitlearn git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: .gitignore
deleted: git.log
➜ gitlearn git:(master) ✗ echo '*.log' > .gitignore
➜ gitlearn git:(master) ✗ git add .
➜ gitlearn git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: .gitignore
deleted: git.log

# 提交文件
➜ gitlearn git:(master) ✗ git commit -m "test remove logs files - remove git.log"
[master 9118876] test remove logs files - remove git.log
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 .gitignore
delete mode 100644 git.log
➜ gitlearn git:(master) git status
On branch master
nothing to commit, working tree clean
# 验证一下git.log文件会不会被git跟踪

# 修改了git.log文件之后,git不会跟踪其变更。
➜ gitlearn git:(master) echo 'test track git.log??' > git.log

➜ gitlearn git:(master) git status
On branch master
nothing to commit, working tree clean

注意:

运行 git rm 命令并不能删除文件!是从 git 中删除,使 git 不在跟踪其变化。

有时候,使用 git rm 命令并不能使文件不被 git 跟踪,因为在缓存中还跟踪了文件。 这使,可以使用 git rm -rf --cached 命令使 git 不跟踪文件。

git rm 命令也可以使用 glob 模式。

# 查看文件的修改内容

git 提供了一个命令 git diff 可以查看文件修改了那些地方.

假设:我们修改了一个文件,我要看一下修改的内容 (此时还没有提交。)

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  gitlearn git:(master) echo 'test diff' > README.md

## 查看不同
### 比较的是: 工作目录中当前文件和暂存区域快照之间的差异。
➜ gitlearn git:(master) ✗ git diff
diff --git a/README.md b/README.md
index 2afbeed..b69a2df 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1 @@
-test git跟踪文件
-test 再次修改已经被跟踪的文件
+test diff

使用 --staged 选项可以比较 已暂存文件与最后一次提交的文件差异。
请注意, git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件,运行 git diff 后却什么也没有,就是这个原因。

# 移动文件

可以使用 git mv 命令来移动文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  gitlearn git:(master) ✗ git mv README.md README.md.md
➜ gitlearn git:(master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README.md.md

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: README.md.md

这个命令就相当于:

1
2
3
$ mv README.md README.md.md
$ git rm README.md
$ git add README.md.md

本篇文章就到这里啦,下一篇~git 的时光机

# 最后

期望与你一起遇见更好的自己

期望与你一起遇见更好的自己