工作区、暂存区和版本库

学习Git,首先要明白一个重要的基本概念:工作区(Working Directory)、暂存区(Staging Area)和版本库(Repository)。版本库里保存的是我们提交的多个版本的代码快照,如果你想查看某个版本的代码,可以通过git checkout检出命令将版本库里这个版本的代码拉取出来,释放到工作区。在工作区里你可以浏览某一个版本的代码、修改代码。如果你想把你的修改保存到版本库中,可以先将你的修改保存到暂存区,接着修改,再保存到暂存区,等你修改真正完成,再统一将暂存区里所有的修改提交到版本库中。

这个时候你可能就犯嘀咕了:为什么还需要一个暂存区(Staging Area)呢?将工作区的修改直接提交(Commit),保存到版本库中岂不是更方便?其实,我也曾思考过这个问题,个人理解是:对于一个版本库来说,你的任何一个提交,包括修改、添加文件、删除文件等操作都会有一个记录,而在实际工作中,对于一个工程师来说,他在开发一个功能时,可能会分成很多步,如果每一小步都去提交一次,意义不是很大,而且不是一个完整的功能,别人可能就搞不懂你的提交到底实现了什么功能。比如我有一个提交:将大象放到冰箱里,别人一看可能就知道是怎么回事。但在实际的开发过程中,我可能分三步开发:

  • 打开冰箱门
  • 把大象放到冰箱里
  • 关上冰箱门

如果将每次很小的修改都做一次提交,就不是很合适,从原则上讲,我们的每一次提交,都是一个里程碑:要么新增了一个功能、要么修改了一个Bug,要么优化了一个功能。在实际开发中的每一小步,都可以先保存到暂存区,等整体功能完成后,再统一提交比较合理。

保存在暂存区的修改,如果你不满意,还可以取消掉,或者重新修改,都没有问题。如果你一旦把暂存区里的修改提交到版本库中,就会在版本库中留下记录:你再将它删除,也会再留下一个删除的记录。总之,你提交到版本仓库中的每一次修改,无论是添加一个文件、修改一个文件,还是删除一个文件,都是一个提交,都一一记录在案,如果你多次修改,多次撤销,就会在版本库中留下凌乱的提交记录。因此,我们要学会合理地使用暂存区,将暂存区里的修改检测无误后,再提交就比较好。

接下来我们举个例子,给大家演示一下如何使用暂存区去取消修改。我们首先修改main.c文件,添加新的文本:step1: close fridge door,修改后的main.c就变成了下面的样子:

root@ubuntu:/home/demo# cat main.c 
#include <stdio.h>

int main (void)
{
    return 0;
}
step1: close fridge door

使用git status命令查看当前工作区的状态:

root@ubuntu:/home/demo# 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:   main.c

no changes added to commit (use "git add" and/or "git commit -a")

因为我们对工作区的main.c做了修改,跟版本库中的main.c相比,文件的内容发生了变化,所以你会看到上面的提示信息:main.c发生了修改,你可以使用git add 将这个修改保存到暂存区,或者直接使用git commit -a直接提交到版本库中,我们选择使用git add命令将这个修改保存到暂存区。

root@ubuntu:/home/demo# git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   main.c

此时再用git status命令查看工作区的状态,Git会提示你:main.c的修改保存到了暂存区,你可以使用git commit命令将暂存区的修改提交到本地仓库,或者使用git reset命令将main.c的修改从暂存区回退到工作区。我们选择回退到工作区。

root@ubuntu:/home/demo# git reset HEAD main.c
Unstaged changes after reset:
M    main.c

这个时候我们再使用git status命令去查看工作区的状态,你会发现又回到了我们使用git add main.c之前的状态。

root@ubuntu:/home/demo# 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:   main.c

no changes added to commit (use "git add" and/or "git commit -a")

此时,你可以选择继续修改这个文件,然后使用git add重新保存到暂存区,或者放弃修改,使用git checkout —main.c 命令将这次修改丢弃掉。

root@ubuntu:/home/demo# git checkout -- main.c
root@ubuntu:/home/demo# git status
On branch master
nothing to commit, working tree clean

git checkout命令的作用是将版本库中某个版本的文件快照释放到工作区,git checkout后面可以跟很多参数,比如:分支名、标签名、某个具体的commit ID哈希值,它们本质上都是一样的,都是某个具体的提交节点,都是版本库中的某个版本。将该版本的文件快照释放到工作区后,工作区和版本库中的文件内容都是一样的了,此时我们再使用git status命令,Git就会提示你:当前工作区没有修改,是干净的。

小结:
git status是一个很有用的命令,当你对Git不是很熟时,不知道如何操作时,可以先使用git status查看一下当前工作区的状态,根据命令的提示信息,选择继续往前操作或往后撤销回退。

《Linux三剑客》视频教程,从零开始快速掌握Linux开发常用的工具:Git、Makefile、vim、autotools、debug,免费赠送C语言视频教程,C语言项目实战:学生成绩管理系统。详情请点击淘宝链接:Linux三剑客