# 创建版本库

在前边的例子中,我们已经创建过的版本库,这里我们来回顾一下,首先我们找了一个合适的地方创建了一个空目录,然后我们使用 git init 命令让该目录变成 git 仓库,这里我们看到 git 输出信息 Initalized empty Git repository,这是告诉我们这是一个空仓库。

注意,细心的你可能发现在 git_demo 目录下有一个.git 目录(该文件是隐藏的,需要在查看中勾选隐藏的项目选项) .git 目录是 git 用来管理版本库的,如果乱改该目录下的文件 git 仓库会被破坏。

命令:git init

# 文件提交

文件提交是将暂存区中所有的内容添加到分支中,在上面的例子中我们使用命令 git commit -m “create 1.c” 将暂存区的文件提交到分支 master,这里我们使用了参数 -m 参数后引号的内容是此次提交的标注。然后我们用 git status 查看状态,此时工作区内容已经干净了,暂存区的文件已经提交到了分支上,所以 git 告诉我们 working tree clean,并且我们用 git log 命令查看 git 的日志信息,在日志信息中明确记录了我们有过一次提交记录,此记录中注明了”create 1.c”。

命令:git commit

# 版本回退

到目前位置,我们的 git 上只提交了一个版本,现在我们对文件进行修改,我们用编辑器在 1.c 中写一个 hello world 程序然后保存退出,然后我们用 git status 查看状态,这里 git 输出信息文件处于 modified 状态,表示文件仅被修改,但没有做其它任何操作。接着我们将文件添加到暂存区并提交,然后再 git status 和 git log 看一下。

此时我们可以看到 git log 的输出信息上出现了两次 commit 这就是我们提交的两次版本,第一次我们创建了 1.c 文件然后提交,第二次我们在 1.c 中写入了一个 hello world 程序提交。我们不妨对 1.c 文件做第三次修改,我们封装一个函数专门用来输出 hello world,然后提交。

这时,我们 master 分支上已经有三次提交记录了,我们看到这里我们的 1.c 有三个版本,分别为”create 1.c” “write hello world demo” “ add func_p”。那么如果我们不想要这个封装好的函数,要从”add func_p” 这个版本直接回到上一个版本”write hello world demo” 应该怎么做呢?别急,我们先看看 git log 给我们输出的信息都是什么。 首先我们看到日志的第一行

commit 8abb13d8fd51b4bd6f6b9aaedac4add3ed368de8

这是 commit id 即版本号,当然你这串数字肯定和我的不一样,这一串数字是哈希值。 然后我们看第二行 Author ,Author 是提交者的信息即我们安装 Git 后最先设置的 Username email@example.com 接着我们看第三行 Date , 很明显了,这一行就是提交的时间 最后我们看到了 add func_p 这就是我们在提交的时候标注的信息 当然,聪明的你一定想说那关键信息不就是版本号和提交标注吗?你直接给我输出这两个不就可以了吗,输出这么大堆东西看着头都大了。这肯定是可以的,我们在 git log 后加个参数 --pretty=oneline 试一下

你看,加上参数之后 git 就只给我们输出版本号和标注了。了解了这些之后,我们回归正题,如何将版本退回到”wirte hello world demo” 在我们前面的学习中我们知道 git 里有个指针 HEAD 指向当前版本,我想你肯定猜到了,我们只需要让该指针指向上一个版本就可以了。那我们来学一个新命令 git reset --hard HEAD^ 这个命令是将 HEAD 指针指向上一个版本,那如果是指向上上个版本就是 git reset --hard HEAD^^。我们先来试一下,在命令行中输入这个命令

这是 git 输出信息 HEAD 指针已经指向了 write hello world demo 这个版本,为了验证是不是真的退回了上一个版本,我们 cat 1.c 一下看看

从输出结果我们看到已经成功退回上一版,这时候你就有一个疑问了,要是以后版本多了比如出现了上千的版本那一直 HEAD^(若干个) 岂不是非常麻烦,这不必担心,如果是退回非常多个版本比如 20 个,那其实并不需要打 20 个 ^,我们可以这么写 HEAD~20,而且还有另一个办法,在前面我们说到了 commit id ,那我们是不是也可以通过 commit id 来退回版本呢?答案是肯定的,我们试试用 commit id 来退回到最初的版本。我们先 git log 看一下,我们看到 git log 的输出信息只剩下两个版本了,刚刚退回前的”add func_p” 版本已经不见了。我们找到”create 1.c” 版本,将 commit 后边的 id 号复制后填写到 git reset --hard id。在图中你看到我并没有填写整一串 id,这是因为 git 会自动帮我们找,所以我们填写前边几位就行了,当然也不能只填写一两位,在版本多了情况下 git 可能会找到多个版本,这时就无法确定是哪一个了。

这个时候我们再来看一下 git log 发现只剩下一个版本了,但是这个时候你后悔了,你想回到”add func_p” 版本,那怎么办呢?还有办法吗?

当然办法还是有的,在你还没有关闭命令行的情况下你只需要往前翻,找到之前”add func_p” 的 commit id 就可以了,就像下边这样。我们找到了”add func_p” 的 commit id 并且跳了回去,此时我们不仅直接跳回了”add func_p” 版,而且顺带把中间版本也找回来了。

那如果说你在下班前退回了某个版本,然后关了笔记本电脑,回到家的时候你后悔了那怎么办?还有办法吗?在 git 中当然有办法了,git 给我们提供了一个命令叫 git reflog

命令: git reflog

这个命令记录了你每次命令。我们先退回到最初版,然后用 git reflog 看看信息。

在退回最初版后我们在 git reflog 中依然能够找到”add func_p” 版本的 commit id:8abb13d,那我们就 git reset --hard id 回到”add func_p” 版本:git reset --hard 8abb13d 然后 git log 看一下,成功回到”add func_p” 版本。

# 撤销修改

你在写完 1.c 后给这个 hello world 程序加了一行注释,正准备 add 的时候你觉得这行注释完全没有必要存在,这个时候你想撤销刚刚的修改应该怎么做呢?我们只需要对 1.c checkout ,怎么写这行命令呢?我们在命令行输入

命令:git checkout -- 1.c

注意,这里要用两个 - 如果少了一个 - 的话表示的是切换到一个叫 1.c 的分支。

我们看到这里刚刚添加的注释已经被撤销了。那如果说这个文件已经 add 到了暂存区,但是还没 commit,这个时候你想撤销之前添加的注释,首先我们应该把暂存区的修改退回到工作区,然后在撤销刚刚对 1.c 的修改。具体怎么做,我们用命令 git reset HEAD 1.c 将 1.c 从暂存区中重新放回工作区,然后再 git checkout -- 1.c 撤销工作区中 1.c 的修改。

在图中我们看到 git reset HEAD 1.c 之后暂存区中的 1.c 已经重新放回了工作区,而工作区的 1.c 是改动过的。这时我们在对 1.c 进行 git checkout -- 1.c 操作,撤销对 1.c 的修改,我们看看结果。

我们看到 1.c 中的注释已经成功撤销了。

# 删除文件

某一天,你手滑把 1.c 给删了,那 git 是知道你把文件删了的,此时工作区和版本库是不一致,我们使用 git status 命令,git 会告诉你 1.c 被删除了

这时你想恢复文件,因为在版本库中 1.c 文件还是存在的,你只需要执行命令

命令:git checkout -- 1.c

将版本库中版本恢复到工作区就可以了。是不是这样呢?我们执行一下命令,执行 git checkout -- 1.c 命令后 1.c 文件确实恢复了。

如果说你不是手滑删错了而是确实想把 1.c 文件删掉,那你在工作区删除 1.c 后你需要执行命令

命令:git rm 1.c

然后 commit 才能将 1.c 从版本库中删除。