修改最后一次提交
- 仅修改提交信息
只想修改最近一次提交的提交信息,使用git commit --amend
命令,进入文本编辑器(vim),里面包含最近一条提交信息,可修改。保存并关闭后,替换最近一条提交信息 - 添加后修改文件更改提交快照
- 修改文件后运行
git add
或git rm
一个已经追踪的文件 - 运行
git commit --amend
使当前暂存区做为新的提交快照
- 修改文件后运行
重置揭秘
理解reset
和 checkout
的最简单的方法,就是用 Git 思维框架(可将其作为内容管理器),来管理并操作三棵不同的树。树
:实际上是“文件的集合”,而不是指数据结构。
三棵树
树 | 说明 |
---|---|
HEAD | 上一次提交的快照,下一次提交的父结点 |
Index | 预期的下一次提交的快照 |
Working Directory | 实际的文件,可编辑。类似沙盒 |
HEAD
HEAD是当前分支引用的指针,它总是指向该分支上的最后一次提交。这表示 HEAD 将是下次提交的父结点。理解 HEAD 最简单方式,将它看做你的上一次提交快照。
查看快照实际的目录列表git cat-file -p [commitID]
查看提交对象1
2
3
4
5
6git 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 filegit ls-tree -r [commitID]
查看提交时的快照的文件1
2
3git 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
2git ls-tree -r HEAD
100644 blob d917a4f56af780cc5033241e4c274c2386a0f9be a.txt工作目录
HEAD,Index 两棵树以一种高效但不直观的方式,将它们的内容存储在.git
文件夹中,工作目录会将它们解包为实际的文件,可编辑,可把其当做沙盒。
工作流程
Git 就是通过操作这三棵树来以连续的状态记录项目的快照。
具体流程说明:
- 新目录,有一个文件 file.txt V1 版本,
git init
,创建一个 Git 仓库,其中 HEAD 引用指向未创建的分支(master 还不存在) - 提交该文件,
git add
,来获取目录中的内容,并将其复制到索引 git commit
,取得索引中的内容并将其保存为一个永久的快照,然后创建一个指向该快照的提交对象,最后更新master
来指向本次提交。
修改然后提交,也将经历相同的过程:
工作目录修改文件 ,V2 版本.
git status
,显示Change not staged for commit
,并被标记为红色,因为,该文件在索引和工作目录中存在不同。git add
将 V2 版本文件,暂存到索引中。git status
,显示Changes to be committed
,并标记文件绿色,因为,预期的下一次提交与上一次提交存在不同。git commit
,把索引中的内容保存为提交对象所指向的快照,更新master
来指向本次提交。
切换分支,克隆过程
- 检出一个分支,修改 HEAD 指向新的分支引用
- 把分支最后一次提交的快照,填充到索引
- 将索引中的内容,解包复制到工作目录中
重置作用(reset)
为了演示例子,假设我们再次修改了file.txt
文件并第三次提交。现在提交历史样子:
其实reset
只做了三个基本的操作,操作那三颗树(HEAD,Index,Work Directory)
第 1 步:移动 HEAD (–soft)
reset
做到第一件事:移动 HEAD 的指向,而不是改变 HEAD 自身(checkot,会改变 HEAD)。reset
,移动 HEAD 指向的分支;checkout
,移动 HEAD 自身,让自身指向某分支。
例如:如果HEAD
设置为master
分支,git reset 9e5e64a
将会使master
指向9e5e64a
.
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
指向的当前快照的内容来更新索引。
如果指定--mixed
选项,reset
将会停在着,这也是默认行为,如果没有指定任何选项(git rest HEAD~
等同于 git reset --mixed HEAD~
)git reset --mixed
,撤销上一次提交,取消暂存所有的东西,即,回滚到所有git add
和git commit
命令执行之前
第 3 步:更新工作目录(–hard)
使用--hard
选项, reset
会让工作目录内容和索引一样。即撤销了最后的提交、 git add
和git commit
命令,以及工作目录中的所有工作。
检查(checkout)
不带路径
git checkout [branch]
与git reset --hard [branch]
非常相似,更新所有三棵树,使其看起来像[branch]
。区别:checkout
对工作目录是安全的,它会通过检查(并试着简单合并一下)来确保不会将已经更改的文件弄丢。reset --hard
,则不做检查,直接全面的替换所有东西reset
会移动HEAD
分支的指向,而checkout
只会移动 HEAD 自身来指向另一个分支。
带路径
checout file
和reset
一样不会移动 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】引入的修改的新的提交,然后引用到当前分支上。