git基础(8)-重置揭秘

修改最后一次提交

  • 仅修改提交信息
    只想修改最近一次提交的提交信息,使用 git commit --amend 命令,进入文本编辑器(vim),里面包含最近一条提交信息,可修改。保存并关闭后,替换最近一条提交信息
  • 添加后修改文件更改提交快照
    1. 修改文件后运行git addgit rm一个已经追踪的文件
    2. 运行git commit --amend使当前暂存区做为新的提交快照

重置揭秘

理解resetcheckout的最简单的方法,就是用 Git 思维框架(可将其作为内容管理器),来管理并操作三棵不同的树。
:实际上是“文件的集合”,而不是指数据结构。

三棵树

说明
HEAD 上一次提交的快照,下一次提交的父结点
Index 预期的下一次提交的快照
Working Directory 实际的文件,可编辑。类似沙盒
  • HEAD
    HEAD是当前分支引用的指针,它总是指向该分支上的最后一次提交。这表示 HEAD 将是下次提交的父结点。理解 HEAD 最简单方式,将它看做你的上一次提交快照。
    查看快照实际的目录列表

    • git cat-file -p [commitID] 查看提交对象

      1
      2
      3
      4
      5
      6
      $ git cat-file -p HEAD
      tree 3ed9fb9aba37ab6c9af1484fdec8f9edb5bd4e0b
      parent 331a960f747598a4b2119587d49a35870db888c5
      author liuzhenjiang <1433539514@qq.com> 1550824953 +0800
      committer liuzhenjiang <1433539514@qq.com> 1551059104 +0800
      change the commit and file
    • git ls-tree -r [commitID] 查看提交时的快照的文件

      1
      2
      3
      $ git ls-tree -r HEAD

      100644 blob abe3a110e1ebb15baea3ba02acda8deeb28437bb a.txt
  • 索引
    索引是预期的下一次提交,也可引用为 Git 的暂存区域,也就是运行git commit 时 Git 看起来的样子。
    Git 将 上一次检出到工作目录中的所有文件填充到索引区,之后会将其中一些文件替换为新版本,git add 后,会替换暂存区内文件,并有新的 SHA-1 校验和,接着通过git commit 将它们转换为树来作为新的提交。
    git ls-files -s ,显示当前索引的样子

    1
    2
    $ git ls-tree -r HEAD
    100644 blob d917a4f56af780cc5033241e4c274c2386a0f9be a.txt
  • 工作目录
    HEAD,Index 两棵树以一种高效但不直观的方式,将它们的内容存储在.git文件夹中,工作目录会将它们解包为实际的文件,可编辑,可把其当做沙盒。

工作流程

Git 就是通过操作这三棵树来以连续的状态记录项目的快照。

工作流程

具体流程说明:

  1. 新目录,有一个文件 file.txt V1 版本,git init,创建一个 Git 仓库,其中 HEAD 引用指向未创建的分支(master 还不存在)
  2. 提交该文件,git add ,来获取目录中的内容,并将其复制到索引
  3. git commit,取得索引中的内容并将其保存为一个永久的快照,然后创建一个指向该快照的提交对象,最后更新master来指向本次提交。

修改然后提交,也将经历相同的过程:

  1. 工作目录修改文件 ,V2 版本.git status,显示 Change not staged for commit,并被标记为红色,因为,该文件在索引和工作目录中存在不同。

    edit file

  2. git add 将 V2 版本文件,暂存到索引中。git status,显示Changes to be committed,并标记文件绿色,因为,预期的下一次提交与上一次提交存在不同。

    git add

  3. git commit,把索引中的内容保存为提交对象所指向的快照,更新master来指向本次提交。

    git commit

切换分支,克隆过程

  1. 检出一个分支,修改 HEAD 指向新的分支引用
  2. 把分支最后一次提交的快照,填充到索引
  3. 将索引中的内容,解包复制到工作目录中

重置作用(reset)

为了演示例子,假设我们再次修改了file.txt文件并第三次提交。现在提交历史样子:

commit history

其实reset只做了三个基本的操作,操作那三颗树(HEAD,Index,Work Directory)

第 1 步:移动 HEAD (–soft)

reset做到第一件事:移动 HEAD 的指向,而不是改变 HEAD 自身(checkot,会改变 HEAD)。reset,移动 HEAD 指向的分支;checkout,移动 HEAD 自身,让自身指向某分支。
例如:如果HEAD设置为master分支,git reset 9e5e64a 将会使master指向9e5e64a.

get reset HEAD
git resset --soft, 仅使移动 HEAD,它的本质是:撤销上一次git commit命令。当在运行git commit时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支,使分支指向该次提交。
git reset HEAD~(重置回 HEAD 的父结点),其实就是把分支移动回上一次提交的位置,而不会改变索引和工作目录。然后更新索引并再次git commit,来完成git commit --amend所要做的事情。

第 2 步:更新索引(–mixed)

git status ,以绿色,标记HEAD索引之间的区别。
接下来,reset会使用HEAD指向的当前快照的内容来更新索引。

reset --mixed

如果指定--mixed选项,reset将会停在着,这也是默认行为,如果没有指定任何选项(git rest HEAD~ 等同于 git reset --mixed HEAD~
git reset --mixed,撤销上一次提交,取消暂存所有的东西,即,回滚到所有git addgit commit命令执行之前

第 3 步:更新工作目录(–hard)

使用--hard选项, reset会让工作目录内容和索引一样。即撤销了最后的提交、 git addgit commit命令,以及工作目录中的所有工作。

reset--hard

检查(checkout)

不带路径

  • git checkout [branch]git reset --hard [branch]非常相似,更新所有三棵树,使其看起来像[branch]。区别:

    • checkout对工作目录是安全的,它会通过检查(并试着简单合并一下)来确保不会将已经更改的文件弄丢。reset --hard,则不做检查,直接全面的替换所有东西

    • reset会移动 HEAD分支的指向,而checkout只会移动 HEAD 自身来指向另一个分支。

      HEAD move

带路径

checout filereset一样不会移动 HEAD 自身。它会git reset --hard [branch] file那样:用该次提交中的那个文件来更新索引,但是也会覆盖工作目录中对应的文件。

总结

下面是速查表,列出了命令对树的影响。HEAD列中的"REF"表示该命令移动了 HEAD 指向的分支引用。"HEAD"表示只移动了 HEAD 自身。 WD Safe?列,表示对工作目录是否安全?

command HEAD Index Workdir WD Safe?
Commit Level
reset –soft [commit] REF NO NO YES
reset [commit] REF YES NO YES
reset –hard [commit] REF YES YES NO
checkout [commit] HEAD YES YES YES
File Level
reset [commit] [file] NO YES NO YES
checkout [commit] [file] NO YES YES NO

git revert

git revert [commit],会生成一个撤销了【commit】引入的修改的新的提交,然后引用到当前分支上。