配置本地git环境时候,首先在 git config 中需要声明 user.name 和 user.email,因为在以后每次commit时候都需要该信息来记录
Git 结构和运行原理
首先我们需要了解一下git的运行原理,本地git的结构如下所示:
其中:
1、Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间
2、WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间
3、.git:存放Git管理信息的目录,初始化仓库的时候自动创建
4、Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区
5、Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)
6、Stash:是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态
了解了以上的基本结构,我们现在可以尝试创建本地仓库并通过实验来了解git操作了。
一、Create --- 创建仓库
先cd到一个我们想要存放代码的位置,在该路径下使用命令 git init 初始化git仓库,轻松创建成功。并且可以看到Git管理信息目录.git文件,如下所示:
二、Add --- 添加操作
现在添加一个 mdr_test 文件,文件内容如下:
可以看到这就是一个简单的斐波那契数列(Ai = Ai-1 + Ai-2),另外还有数据存储日期
我刚刚只是完成了文件的编辑,通过使用命令 git status 可以查询WorkSpace的状态,从中得知 mdr_test 文件并没有被跟踪——track,并且提示可以使用 git add 来进行跟踪。然后使用 git add mdr_test 命令将文件添加到待提交区(暂存区)——Index / Stage,这时再通过 git status 查看状态,可以发现已经出现了 "new file : mdr_test" 的提示。如下所示:
最后,使用命令 git commit -m "XXX" 来提交更新就可以了。其中 -m 后面的 “XXX” 为对于本次更新的描述(message的缩写),例如 "add function" / "bug fix" 之类的即可。需要提到的是,-m 的更新描述是必须写的,否则无法提交更新。通过 commit 操作,所有的更新已经从 Stage 同步到本地仓库 —— Local Repo 中了。此时再次查看 git status,返回的信息是 nothing to commit,说明已无其他待提交。如下所示:
三、Update --- 更新操作
假设我们现在需要更新刚刚提交的 mdr_test 文件中的data数据,将斐波那契数列换成大衍数列(N为奇数:An = (n * n -1) / 2,N为偶数:n * n / 2)
而这时,只是vim完成了数据修改,通过 git status 可以看到本次修改并没有进入暂存区,只是发现了modified。如下所示:
同样道理,我们使用 git add 和 git commit 操作来完成对本次数据更新的提交,同步到 Local Repo 中。如下所示:
需要注意的是,无论是添加文件还是修改数据,通过 commit 更新到 Repo 的都只是缓存区的变动。也就是说,无论在 WorkSpace 做了何种修改,只有没有经过 git add 操作提交到缓存区,就无法在 git commit 的时候更新到仓库中。
那么如果只有缓存区的更新能够被同步到仓库,如何确认 WorkSpace 中的文件与 Stage 中的文件相同呢?这可以使用 diff 命令。比如我们再次将 mdr_test 中的 data 更新成等比数列(An = 2^n),如下所示:
此时,缓存区中 mdr_test 的 data 依然是大衍数列,而工作区中的data已经更新成等比数列,使用 diff 可以看到两者的不同。当我们将本次数据更新通过 git add 提交后,再使用 git diff 就找不到任何不同了(也就是没有返回),并且能够在 git status 中看到本次 mdr_test 文件的 modified,如下所示:
当然,也可以直接diff WorkSpace 与 Repo 中的不同,命令为 git diff HEAD。因为刚才我已经将大衍数列变成等比数列更新到缓存区,所以只需要 commit 一下就可以让 WorkSpace 和 Repo 一致了。操作流程如下所示:
四、Revoked --- 撤销操作
撤销操作会出现在三个地方,即 WorkSpace 阶段、Stage 阶段、Repo 阶段,下面分别来说
1、撤销 WorkSpace 阶段更新
我们将 mdr_test 文件中的 data 先从等比数列改回斐波那契数列,然后通过 cat 和 git status 可以看到这时候文件是在 WorkSpace 中发生的数据更新。这时候,当然我们可以手动的修改文件数据恢复到等比数列,但为了避免哪里忘记恢复,保证数据一致性,往往使用 checkout 来检出文件的历史版本,即使用命令:git checkout mdr_test 来回滚到 mdr_test 在 Repo 中最新的一个版本,如下所示:
需要注意的是,checkout 是历史版本检出,将直接覆盖掉本地文件,当然也包括在最新版本之后所有的修改和更新内容,所以在检出前一定要确认可以检出,并适当做好本地文件备份
2、撤销 Stage 阶段更新
同样我还是先把 mdr_test 的 data 中等比数列改成斐波那契数列,然后这次将更新 add 到缓存区。用 cat 和 status 可以看到 data 更新已经提交到 Stage。此时,我们使用命令:git reset HEAD mdr_test 用来恢复 Stage 中的数据。这行命令的意思其实就是从 Repo 的最新版本 HEAD 中拿出文件 mdr_test 来 reset更新掉 Stage 中的 mdr_test。更新后,再次使用 git status 查看状态会发现,Stage 中的更新已经取消,而 WorkSpace 中的更新依然存在。操作过程如下所示:
3、撤销 Repo 阶段更新
在撤销 Repo 阶段更新之前,我们需要先使用 git log 命令查询 git commit 的历史版本。可以看到我的 Local Repo 中有三个版本,分别是一个 add 和两次 update,如下所示:
Repo 的撤销有两种方式:使用 HEAD 指针和使用 commit id
在 Git 中,HEAD 指针指向当前最新提交,当前版本我们定义为 HEAD^,也可以表示为 HEAD~1,再前一个版本定义为 HEAD^^,也可以表示为 HEAD~2,那么往前的第n个版本自然就是 HEAD~n。所以对于我们的情况来说,只有三个版本,我们使用命令:git reset --hard HEAD^^ 就可以回滚到第一个版本。
这里,顺便对比一下是否存在 --hard 对 git reset 的影响
首先,我们使用命令:git reset HEAD^
可以看到经过 reset 后,git log 查询到之前的 commit 也被抹掉了,只留下第一次提交数据的记录,并且 Stage 中无其他更新,其中 WorkSpace 中的数据是斐波那契数列。此处,可能有同学存在一个疑问,reset 后更新覆盖的仅仅是 Stage 中的数据还是 Stage 和 WorkSpace 中的数据呢?这个问题在这里看不清是因为我们的 reset 后的数据和第一次 add 后的数据一致,实践出真知,所以我们再来做一个实验。
首先我们先设计一下,我们对 mdr_test 做两次修改,第一次日期更新:日期更新到2017-09-14,第二次数据更新:在斐波那契数列后延续加一项,并且每一次都通过 add 和 commit 提交到仓库中,如下图所示:
此时 Repo 中有三个版本,我们这回只 reset 到上一个版本,也就是只修改了日期,但没有修改数据的版本,然后通过 cat 发现 WorkSpace 中还是日期和数据均修改后的版本,并且 status 中标红的、提示没有 add 的 modified 也说明相同的结论:那就是 reset 只会将 Stage 中的版本回滚到 Repo 指定的版本,而不会干扰到 WorkSpace 中的文件。如下所示:
那好了,再让我们来看看 git reset --hard HEAD^ 是什么样的效果
首先,我们还是提交了一次斐波那契数列的数据更新,将 Repo 恢复刚才的样子,然后使用命令:git reset --hard HEAD^ 来恢复到前一个版本。结果是 git log 中也是将数据更新的 commit 抹掉,但是不同的是连 WorkSpace 中的 mdr_test 也被改成了第二个版本,也就是只有日期更新,没有数据更新的版本,而且可以通过 status 看到无待更新内容。这证明包含 --hard 参数的 reset 是彻底的,将同时对 WorkSpace 和 Stage 生效。如下所示:
下面我们介绍一下 git reflog 命令
git reflog 与 git log 的区别是:git log 只包含当前分支中的 commit 记录,而 git reflog 会记录这个仓库中所有分支的所有更新记录,包括已经撤销的更新 commit 记录。
我们除了通过 HEAD 指针的方式去恢复到以前的版本,另外一种重要的方式就是直接指定版本号,而 reflog 能够给我们提供完整的版本信息。比如此时我们想要恢复到等比数列(b438a33)的版本,只需要使用命令:git reset --hard b438a33 就可以了,如下所示:
这里使用的参数 --hard 还有个对应的参数 --soft,区别是:
hard:撤销并删除相应的更新
soft:撤销相应的更新,并把这些更新的内容放到 Stage 中
五、Delete --- 删除操作
1、rm:只在 WorkSpace 删除相应文件,如果想提交更新,以后得自己 add 到 Stage
2、git rm:在删除 WorkSpace 相应文件的同时,将这些删除更新到 Stage 中
如下所示: