# 第 04 天：常用的 Git 版本控制指令

本篇文章将带大家学会几个最重要也最基本的版控工作，其中将包含基本的文件操作如新增、删除、重新命名文件，提交变更 (建立新版本)、查询历史记录等工作。

## 准备工作目录

复习一下上一篇「第 03 天：建立仓库」的内容，我们直接来建立一个本地仓库，即可开始本篇文章的所有练习。

```
mkdir git-demo
cd git-demo
git init
```

## 新增文件

我们在工作目录下放一些文件，至于放什么文件都可以，总之先复制一些现有的文件与目录进到目前的工作目录下。

本篇文章，我将以 [YEOMAN](http://yeoman.io/) 工具，快速产生一个 webapp 范例网站，只要一个指令就可以建立一个完整网站：

```
yo webapp
```

**注**: 关于 [YEOMAN](http://yeoman.io/) 在 Windows 平台的使用，可以参考笔者的文章 [如何在 Windows 平台安装与使用 Yeoman 1.0 相关工具](http://blog.miniasp.com/post/2013/08/11/Yeoman-1-0-Installation-and-Usage-on-Windows.aspx)，该文详述完整的安装与使用过程。

新增了文件之后，如果你还在 Git Shell 介面下，应该会立刻看到如下图的提示：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-0b2071c15af4f90bbf64c9187f69b0c2bcb205a0%2F01.png?alt=media)

也就是以下这段位于路径后面的提示：

```
[master +10 ~0 -0 !]
```

在这段提示的地方，你可以看到几个东西：

* master 代表目前工作目录是 **master** 分支，也是 Git 的预设分支名称。
* 「红色」的数字都代表 Untracked (未追踪) 的文件，也就是这些变更都不会进入版本控制。
* +10 代表有 10 个「新增」的文件
* \~0 代表有 0 个「修改」的文件
* -0 代表有 0 个「删除」的文件

如果要将这些新增的文件加入到 Git 版本控制，你必须下达以下指令：

```
git add .
```

如此一来，这个工作目录下所有的文件、目录与子目录下的所有文件，全部都会被加入到这个 Git 工作目录的【索引】或【快照】之中。请注意: 此时并没有建立任何版本，只是告知 Git 这些文件「即将」被加入 Git 版本库而已。

如下图示，是我这边执行完 `git add .` 之后的结果，这里所发生的 warning 消息不是很严重，有兴趣了解的人可以参考笔者的另一篇文章: [Git 在 Windows 平台处理断行字元 (CRLF) 的注意事项](http://blog.miniasp.com/post/2013/09/15/Git-for-Windows-Line-Ending-Conversion-Notes.aspx)。

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-a9408d5f30f7bb1aa0969969d0dd3d9937b76afb%2F02.png?alt=media)

不过有趣的地方在于，原本「红色的数字」现在却变成了「绿色的数字」，这里所代表的意义是：

* 「绿色」的数字都代表 Staged (准备好) 的文件，也就是这些变更才会进入版本控制。
* +23 代表有 23 个「新增」的文件将被建立一个版本
* \~0 代表有 0 个「修改」的文件将被建立一个版本
* -0 代表有 0 个「删除」的文件将被建立一个版本

但原本不是只有 +10 (红色) 而已吗? 怎么执行完后变成了 +23 (绿色) 呢？

我们执行 `git reset` 重设一下工作目录的索引状态，然后再执行一次 `git status` 查询当前工作目录的详细状态，如下图示：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-f5fcd579a8819b628a4df5482d58dc6dea8ec934%2F03.png?alt=media)

你可以发现，这边列出的只有「第一层目录下的文件与目录」而已，因为 git 不会这个时候去查到底目录下到底有多少文件没有被追踪。

当我们执行 `git add .` 之后，再执行一次 `git status` 查询状态，你可以发现连子目录下的文件也都全部被加入了，所以这个数字才会变多，如下图示：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-472d2687699588b1410397115d5eddd8da1588fe%2F04.png?alt=media)

## 新增部分文件

刚刚提到的 `git add .` 指令会自动将所有文件(含子目录的文件)加入到工作目录索引中，有时候我们只想让特定目录或特定文件加入版本，这时你也可以指定特定目录，或利用万用字元来加入文件。

我们再执行一次 `git reset` 重设工作目录的索引状态，然后用 `git add app` 加入 app 这个资料夹与其下的所有文件，还有用 `git add .*` 新增所有「点」开头的文件，如下图示：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-bdd6d0a4445017787e0f03b9fc14a46848bdd249%2F05.png?alt=media)

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-8ca3bf3eb955cecd6789d44d6d82381751aabdb6%2F06.png?alt=media)

在执行的过程中，你应该可以发现，在执行 `git add` 的过程中，Git Shell 提示的文字也有了些变化，现在同时出现了「绿色的数字」与「红色的数字」，所代表的意思跟上述是一样的，是不是非常方便辨识！当你需要知道详情才需要执行 `git status` 指令。

**注**: 详细的指令与参数说明，可以输入 `git help add` 查询完整的文件。

## 提交变更 / 建立版本

这时我们预计要建立一个新版本了，在建立版本之前，我们还是把所有文件给加入吧，请各位再执行一次 `git add .` 命令。

建立版本的指令如下：

```
git commit
git commit -m "版本记录的说明文字"
```

在 Git 版本控制中，所有的版本都必须拥有「版本记录的说明文字」 ( 简称 Log )，不像 Subversion 预设可以签入「没有版本记录说明」的版本。所以当你直接输入 `git commit` 的话，预设会开启 Notepad (记事本) 让你输入这个版本的消息。开启后的文件会有很多 # 符号开头的文字，这些都是注解，不会成为 Log 的一部分。

如下图示，是我输入的消息文字：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-ee65c826f67dbe8519f453b6aec027f9f851260c%2F07.png?alt=media)

当你按下 Ctrl+S 储存这个文字档，这时还不会建立一个新版本，还必须关闭这个 Notepad 视窗，这时才会正式建立版本，如下图示：

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-3a1dc45446ed0b81b49a57febe1005bfd91f036b%2F08.png?alt=media)

建立版本完后，Git Shell 的提示就只剩下 `[master]` 字样了，代表目前已经没有任何要被建立版本的索引或快照。

**注**: 详细的指令与参数说明，可以输入 `git help commit` 查询完整的文件。

## 查询历史记录

由于我们刚刚建立了一个版本，当我们想要查询版本的历史记录，可以输入以下指令：

```
git log
```

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-ea28e4f7987a9bedd64b2bd066cd85d5e10830a8%2F09.png?alt=media)

有时候记录越来越多，你也可以通过以下指令限制输出的版本数量，你只要通过一个減号 ( - ) 与一个数字，就可以限定输出最近几笔记录：

```
git log -10
```

**注**: 详细的指令与参数说明，可以输入 `git help log` 查询完整的文件。

## 删除文件

在 Git 指令列工具中也有个 `rm` 指令，可以用来删除文件。例如我们想删除 `Gruntfile.js` 这个文件，可以输入以下指令：

```
git rm 'Gruntfile.js'
```

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-bda47756e738408c1b6c802f46d664a92c325500%2F10.png?alt=media)

这个 `git rm` 的指令执行的时候，会同时做两件事：

1. 删除工作目录快照的 'Gruntfile.js' 这个文件 (用来标示这个删除文件的动作要列入版本控制)
2. 删除工作目录下的 'Gruntfile.js' 这个实体文件 (代表真的把这个实体文件给删除)

**注**: 详细的指令与参数说明，可以输入 `git help rm` 查询完整的文件。

## 文件更名

在 Git 指令列工具中也有个 `mv` 指令，可以用来变更文件或目录的名称。例如我们想把 `test` 目录更名为 `unit-test` 名称，可以输入以下指令：

```
git mv test unit-test
```

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-bedffae9e43d1d8f0422c0cb0986d59b0fbd957a%2F11.png?alt=media)

**注**: 详细的指令与参数说明，可以输入 `git help mv` 查询完整的文件。

## 显示工作目录的索引状态

刚刚我们一直会看到 `git status` 来显示工作目录的状态，你也可以使用 `git status -s` 来显示较为精简的版本。

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-cc1770931e20f3566137a3d2d6096050a874fe98%2F12.png?alt=media)

**注**: 详细的指令与参数说明，可以输入 `git help status` 查询完整的文件。

## 重置目前的工作目录

我们曾经学过如何利用 `git reset` 重置目前工作目录的索引状态，但请注意，这个指令预设只会重置「索引状态」，那些你用 `git rm` 删除的目录或文件，还是用 `git mv` 更名的目录或文件，通过 `git reset` 都无法把「实体文件」给救回来。

如果想把工作目录也给还原到目前的最新版，则必须输入以下指令：

```
git reset --hard
```

**注**: 详细的指令与参数说明，可以输入 `git help reset` 查询完整的文件。

## 还原其中一个被改坏的文件

如果文件编辑到一半，发现被改坏了，你希望能救回没修改前的版本，这时你可以利用以下指令还原文件：

```
git checkout master Gruntfile.js
```

![image](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-adaf1b3f4790941068c7da9528cda0e9a6f4a027%2F13.png?alt=media)

这段指令的意思是把 `master` 分支中最新版的 Grunefile.js 给还原，由于我先前已经把 Gruntfile.js 给删除了，为了要救回这一个档，可以用这个方式救回。这样可以避免使用 `git reset --hard` 一次把所有文件都给还原了！

请注意，还原的过程也会一并复原工作目录的索引状态喔！

**注**: 详细的指令与参数说明，可以输入 `git help checkout` 查询完整的文件。

## 今日小结

今日的文章，可以说是在 Git 版本控制中不断会重复使用的指令与参数，必须非常熟练才行，接下来的文章，将会详细探讨 Git 版本控制的内部结构，千万不要错过！

我重新整理一下本日学到的 Git 指令与参数：

* git init
* git add .
* git add app/\*
* git add \*.txt
* git status
* git status -s
* git commit
* git commit -m "版本记录的说明文字"
* git log
* git log -10
* git rm '\*.txt'
* git rm 'app/\*.html'
* git mv 'oldname' 'newname'
* git reset
* git reset --hard
* git checkout master 'filename'

## 参考连结

* [BASIC SNAPSHOTTING](http://gitref.org/basic/)

***

* [HOME](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan)
* [回目录](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn)
* [前一天：建立仓库](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn/03)
* [下一天：了解仓库、工作目录、物件与索引之间的关系](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn/05)

***
