时光机穿梭

时光机穿梭

我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容:

1
2
Git is a distributed version control system.
Git is free software.

现在,运行 git status 命令看看结果:

1
2
3
4
5
6
7
8
9
$ 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: readme.txt

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

git status 命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt被修改过了,但还没有准备提交的修改。

虽然Git告诉我们readme.txt被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经记不清上次怎么修改的readme.txt,所以,需要用 git diff 这个命令看看:

1
2
3
4
5
6
7
8
9
10
11
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index d8036c1..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
-Git is free software.
\ No newline at end of file
+Git is a distributed version control system.
+Git is free software.

git diff 顾名思义就是查看difference,显示的格式正是Unix通用的diff格式,可以从上面的命令输出看到,我们在第一行添加了一个distributed单词。

知道了对 readme.txt 作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是 git add

1
$ git add readme.txt

同样没有任何输出。在执行第二步 git commit 之前,我们再运行 git status 看看当前仓库的状态:

1
2
3
4
5
6
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: readme.txt

git status 告诉我们,将要被提交的修改包括readme.txt,下一步,就可以放心地提交了:

1
2
3
$ git commit -m "add distributed"
[master be081a3] add distributed
1 file changed, 1 insertions(+), 1 deletions(-)

提交后,我们再用 git status 命令看看仓库的当前状态:

1
2
3
$ git status
On branch master
nothing to commit, working tree clean

Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。

小结

  • 要随时掌握工作区的状态,使用git status命令。
  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容。

版本回退

现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下:

1
2
Git is a distributed version control system.
Git is free software distributed under the GPL.

然后尝试提交:

1
2
3
4
$ git add readme.txt
$ git commit -m "append GPL"
[master 4a97903] append GPL
1 file changed, 1 insertion(+), 1 deletion(-)

像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。

现在,我们回顾一下 readme.txt 文件一共有几个版本被提交到Git仓库里了:

版本1:wrote a readme file

1
2
Git is a version control system.
Git is free software.

版本2:add distributed

1
2
Git is a distributed version control system.
Git is free software.

版本3:append GPL

1
2
Git is a distributed version control system.
Git is free software distributed under the GPL.

当然了,在实际工作中,我们脑子里怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用 git log 命令查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git log
commit 4a97903782925e467139990d05666391f334e3f1 (HEAD -> master)
Author: zhx2020 <13283457@qq.com>
Date: Mon Apr 20 10:58:45 2020 +0800

append GPL

commit be081a3d7e9a00a9767e57941a06b2d724f0b2f2
Author: zhx2020 <13283457@qq.com>
Date: Mon Apr 20 10:52:34 2020 +0800

add distributed

commit 58b42179049062fbaf8bff9f93265e5a217852a9
Author: zhx2020 <13283457@qq.com>
Date: Mon Apr 20 10:22:26 2020 +0800

wrote a readme file

git log 命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是 append GPL ,上一次是 add distributed ,最早的一次是 wrote a readme file

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上 --pretty=oneline 参数:

1
2
3
4
$ git log --pretty=oneline
4a97903782925e467139990d05666391f334e3f1 (HEAD -> master) append GPL
be081a3d7e9a00a9767e57941a06b2d724f0b2f2 add distributed
58b42179049062fbaf8bff9f93265e5a217852a9 wrote a readme file

好了,现在我们启动时光穿梭机,准备把readme.txt回退到上一个版本,也就是 add distributed 的那个版本,怎么做呢?

现在,我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用 git reset 命令:

1
2
$ git reset --hard HEAD^
HEAD is now at be081a3 add distributed

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL:

小结

  1. HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id
  2. 穿梭前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。
  3. 要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。

工作区和暂存区

Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的 docs 文件夹就是一个工作区

工作区

版本库(Repository)

工作区有一个隐藏目录 .git ,这个不算工作区,而是Git的版本库。

Git的版本库里存了很多东西,其中最重要的就是称为 stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

step1

前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

  • 第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
  • 第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

step2

所以,git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行 git commit 就可以一次性把暂存区的所有修改提交到分支。

step3

小结

  • 暂存区是Git非常重要的概念,弄明白了暂存区,就弄明白了Git的很多操作到底干了什么。
  • 没弄明白暂存区是怎么回事的童鞋,请向上滚动页面,再看一次。

管理修改

现在,假定你已经完全掌握了暂存区的概念。下面,我们要讨论的就是,为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。

你会问,什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。

readme.txt 文件进行修改

使用 git add readme.txt

再次对 readme.txt进行修改

直接 git commit -m "modify readme.txt"

结果:第二次的修改没用被提交

小结

  • 现在,你又理解了Git是如何跟踪修改的,每次修改,如果不用git add到暂存区,那就不会加入到commit中。

撤销修改

错误的更改 readme.txt 的内容

1
2
3
Git is a distributed version control system.
Git is free software distributed under the GPL.
error content!!!

使用 git status 查看一下

1
2
3
4
5
6
7
8
9
$ 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: readme.txt

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

选择

  • 使用 git checkout -- readme.txt 丢弃工作区的修改

更改 readme.txt 文件并且 git add readme.txt 到暂存区

1
2
3
4
5
6
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: readme.txt

选择

  • 使用 git reset HEAD readme.txt 把暂存区的修改撤回,重写放回工作区

小结

  • 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file
  • 当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD <file> ,就回到了场景1,第二步按场景1操作。
  • 已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

删除文件

添加一个新文件test.txt到Git并且提交

1
2
3
4
5
$ git add test.txt
$ git commit -m "add test.txt"
[master 4d7dba8] add test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt

在工作目录删除 test.txt

1
rm test.txt

Git 知道删除了文件,工作区和版本库就不一致了,使用 git status命令查看哪些文件被删除了。

1
2
3
4
5
6
7
8
9
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

deleted: test.txt

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

选择

  • 使用 git rm 删除test.txt,并且 git commit
  • 使用 git checkout -- test.txt 用版本库中的版本替换工作区的版本

小结

命令 git rm 用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。

参考