"动"话 Git (2): 深入理解 git reset 和 git checkout 命令
Synopsis: 在《"动"话 Git》系列的第二篇中,我们将继续通过生动的动态图片和精彩的图解,真正理解 git reset 和 git checkout 并恰当地运用它们
1、复习三棵树架构
我们先复习下第一篇中讲的 Git 三棵树(文件引用的集合)架构:
1.1 查看提交(快照)内容
一般情况(未分离头指针)下,HEAD(头指针)
指向当前分支上的最后一次提交的快照
[root@cscs-100-116-20-141 my_project]# cat .git/HEAD ref: refs/heads/master [root@cscs-100-116-20-141 my_project]# git cat-file -t HEAD commit [root@cscs-100-116-20-141 my_project]# git cat-file -p HEAD tree d73cc53e075d8d54f32ec4041795d5051e8bf46d parent 8bd7143de5d6ce6dd83d742fbb0f85906347decd author Madman <Madman@163.com> 1705474710 +0800 committer Madman <Madman@163.com> 1705479040 +0800 renamed: README -> README.md; rebuild soft link help.txt # 显示了 HEAD 快照实际的目录列表,以及其中每个文件的 SHA-1 哈希值 # 查看指定提交或分支的快照内容: git ls-tree -r 8bd7143 和 git ls-tree -r bugFix [root@cscs-100-116-20-141 my_project]# git ls-tree -r HEAD 100644 blob 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 README.md 120000 blob 42061c01a1c70097d1e4579f29a5adf40abdec95 help.txt 100644 blob b1b716105590454bfc4c0247f193a04088f39c7f init.txt 100644 blob a258e13b63ae84eda30d4d4fc940dc58159e32f7 utils/C1.txt 100644 blob 18b5e4c8c8a5568eb522f1599714946678c2d8bd utils/C2.txt # 可以继续查看快照里面文件版本内容 [root@cscs-100-116-20-141 my_project]# git cat-file -p 5e1b2cf init hello
1.2 查看暂存区内容
staging area/index(暂存区)
是预期的下一次提交的快照。当你 检出(checkout)
一个分支或标签时,它会移动 HEAD
指向该分支或标签,将指向的提交的内容复制到 暂存区
和 工作区
中
[root@cscs-100-116-20-141 my_project]# git ls-files -s 100644 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 0 README.md 120000 42061c01a1c70097d1e4579f29a5adf40abdec95 0 help.txt 100644 b1b716105590454bfc4c0247f193a04088f39c7f 0 init.txt 100644 a258e13b63ae84eda30d4d4fc940dc58159e32f7 0 utils/C1.txt 100644 18b5e4c8c8a5568eb522f1599714946678c2d8bd 0 utils/C2.txt # 可以继续查看快照里面文件版本内容 [root@cscs-100-116-20-141 my_project]# git cat-file -p b1b7161 init
1.3 查看工作区内容
HEAD
和 暂存区
这两棵树以一种高效但并不直观的方式,将它们的内容存储在 .git/
目录中。项目目录下除了 .git 目录外就是你的 working directory(工作区)
了。在你将修改提交到暂存区并记录到历史版本库之前,可以随意更改
[root@cscs-100-116-20-141 my_project]# tree . ├── help.txt -> README.md ├── init.txt ├── README.md └── utils ├── C1.txt └── C2.txt 1 directory, 5 files
为了后面的讲解,我们先提交三次、涉及多个文件的新增、修改、删除操作:
第 1 次提交: 新增 a.txt 文件(v1 版本)
[root@cscs-100-116-20-141 my_project]# echo "version 1 of a.txt" > a.txt [root@cscs-100-116-20-141 my_project]# git add a.txt [root@cscs-100-116-20-141 my_project]# git commit -m "Add a.txt(v1)" [master 90471f3] Add a.txt(v1) 1 file changed, 1 insertion(+) create mode 100644 a.txt
第 2 次提交: 修改 a.txt 文件(v2 版本); 新增 b.txt 文件(v1 版本)
[root@cscs-100-116-20-141 my_project]# echo "version 2 of a.txt" > a.txt [root@cscs-100-116-20-141 my_project]# echo "version 1 of b.txt" > b.txt [root@cscs-100-116-20-141 my_project]# git add a.txt b.txt [root@cscs-100-116-20-141 my_project]# git commit -m "Modified a.txt(v2); add b.txt(v1)" [master c32d77f] Modified a.txt(v2); add b.txt(v1) 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 b.txt
第 3 次提交: 修改 a.txt 文件(v3 版本); 删除 b.txt 文件; 新增 c.txt 文件(v1 版本)
[root@cscs-100-116-20-141 my_project]# echo "version 3 of a.txt" > a.txt [root@cscs-100-116-20-141 my_project]# git rm b.txt rm 'b.txt' [root@cscs-100-116-20-141 my_project]# echo "version 1 of c.txt" > c.txt [root@cscs-100-116-20-141 my_project]# git add a.txt c.txt [root@cscs-100-116-20-141 my_project]# git commit -m "Modified a.txt(v3); del b.txt(v1); add c.txt(v1)" [master c2da288] Modified a.txt(v3); del b.txt(v1); add c.txt(v1) 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 b.txt create mode 100644 c.txt [root@cscs-100-116-20-141 my_project]# git log --all --graph --pretty=oneline --abbrev-commit --decorate=short * c2da288 (HEAD, master) Modified a.txt(v3); del b.txt(v1); add c * c32d77f Modified a.txt(v2); add b.txt(v1) * 90471f3 Add a.txt(v1) * d9e659d (develop, bugFix3, bugFix) renamed: README -> README.md * 8bd7143 Add C2.txt | * 6c586a3 (bugFix2) bug fix 02 |/ * 3174178 Add C1.txt; modify README * 1d36d90 init repo
2、git reset
在进行 git reset 实验前,我们再 基于 c2da288
提交 进行一些变更:修改 a.txt 文件(v4 版本)并添加到暂存区; 修改 c.txt 文件(v2 版本)不添加到暂存区; 新增 d.txt 文件(v1 版本)尚未通过 Git 追踪管理
[root@cscs-100-116-20-141 my_project]# echo "version 4 of a.txt" > a.txt [root@cscs-100-116-20-141 my_project]# echo "version 2 of c.txt" > c.txt [root@cscs-100-116-20-141 my_project]# echo "version 1 of d.txt" > d.txt [root@cscs-100-116-20-141 my_project]# 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: a.txt # modified: c.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # d.txt no changes added to commit (use "git add" and/or "git commit -a") [root@cscs-100-116-20-141 my_project]# git add a.txt [root@cscs-100-116-20-141 my_project]# git diff diff --git a/c.txt b/c.txt index 69ef72f..c4f7d1f 100644 --- a/c.txt +++ b/c.txt @@ -1 +1 @@ -version 1 of c.txt +version 2 of c.txt [root@cscs-100-116-20-141 my_project]# git diff --cached diff --git a/a.txt b/a.txt index 8775fe8..e0b4d4b 100644 --- a/a.txt +++ b/a.txt @@ -1 +1 @@ -version 3 of a.txt +version 4 of a.txt
2.1 提交级别的重置
说明: 下面的实验我都是指定了
HEAD^
,实际使用时可以指定一个提交的 SHA-1 哈希值或分支名
(1) git reset --soft
执行 git reset --soft HEAD^
后,Git 只是执行了 reset 的第一个步骤(同时移动 HEAD 与它指向的分支到前一个提交节点),而暂存区和工作区的内容不变,即它本质上是 撤销了上一次提交
[root@cscs-100-116-20-141 my_project]# git reset --soft HEAD^ # 1.HEAD 和 master 指向的提交节点变了 [root@cscs-100-116-20-141 my_project]# git log --all --graph --pretty=oneline --abbrev-commit --decorate=short * c32d77f (HEAD, master) Modified a.txt(v2); add b.txt(v1) * 90471f3 Add a.txt(v1) * d9e659d (develop, bugFix3, bugFix) renamed: README -> README.md * 8bd7143 Add C2.txt | * 6c586a3 (bugFix2) bug fix 02 |/ * 3174178 Add C1.txt; modify README * 1d36d90 init repo # 暂存区未被覆盖,且它与 HEAD 的差异,跟执行 reset --soft 前相比不一样,因为 HEAD 后退了一个提交 [root@cscs-100-116-20-141 my_project]# git diff --cached diff --git a/a.txt b/a.txt index 09b8dbb..e0b4d4b 100644 --- a/a.txt +++ b/a.txt @@ -1 +1 @@ -version 2 of a.txt +version 4 of a.txt diff --git a/b.txt b/b.txt deleted file mode 100644 index 9fd018e..0000000 --- a/b.txt +++ /dev/null @@ -1 +0,0 @@ -version 1 of b.txt diff --git a/c.txt b/c.txt new file mode 100644 index 0000000..69ef72f --- /dev/null +++ b/c.txt @@ -0,0 +1 @@ +version 1 of c.txt # 工作区和暂存区都未被覆盖,所以它与暂存区的差异,跟执行 reset --soft 前相比一样 [root@cscs-100-116-20-141 my_project]# git diff diff --git a/c.txt b/c.txt index 69ef72f..c4f7d1f 100644 --- a/c.txt +++ b/c.txt @@ -1 +1 @@ -version 1 of c.txt +version 2 of c.txt [root@cscs-100-116-20-141 my_project]# git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: a.txt # deleted: b.txt # new file: c.txt # # 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: c.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # d.txt
用途:
- 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。你可以选择再提交一次,但是这样的话提交历史不太干净(修改前后共 2 个提交节点)。还可以使用
git reset --soft HEAD^
然后继续增删改文件,再次运行git commit
即可(修改前后共 1 个提交节点,相当于用一个新的提交替换了旧的提交);或者使用git commit --amend
效果也一样 - 或者在开发一个功能时提交了太多次需要
压缩(squash )
成一次(还可以继续增删改文件)提交,保持提交历史干净。可以使用git reset --soft HEAD~<num>
然后继续增删改文件,再次运行git commit
即可(相当于用一个新的提交替换了 num 个旧的提交))
(2) git reset [--mixed]
恢复到进行 git reset 实验前的状态!
执行 git reset HEAD^
或者 git reset --mixed HEAD^
后,Git 先执行了 reset 的第一个步骤(同时移动 HEAD 与它指向的分支到前一个提交节点),然后执行第二个步骤(用 HEAD 指向的当前快照的内容来 覆盖
到暂存区),而工作区的内容不变,即它本质上是 撤销了上一次提交
,并取消 暂存区
的所有内容
[root@cscs-100-116-20-141 my_project]# git reset HEAD^ Unstaged changes after reset: M a.txt D b.txt # 1.HEAD 和 master 指向的提交节点变了 [root@cscs-100-116-20-141 my_project]# git log --all --graph --pretty=oneline --abbrev-commit --decorate=short * c32d77f (HEAD, master) Modified a.txt(v2); add b.txt(v1) * 90471f3 Add a.txt(v1) * d9e659d (develop, bugFix3, bugFix) renamed: README -> README.md * 8bd7143 Add C2.txt | * 6c586a3 (bugFix2) bug fix 02 |/ * 3174178 Add C1.txt; modify README * 1d36d90 init repo # 2.暂存区被覆盖,它与 HEAD 现在没有差异 [root@cscs-100-116-20-141 my_project]# git diff --cached # 工作区未被覆盖,但是暂存区的内容被覆盖了,所以工作区与暂存区的差异,跟执行 reset --soft 前相比不一样 [root@cscs-100-116-20-141 my_project]# git diff diff --git a/a.txt b/a.txt index 09b8dbb..e0b4d4b 100644 --- a/a.txt +++ b/a.txt @@ -1 +1 @@ -version 2 of a.txt +version 4 of a.txt diff --git a/b.txt b/b.txt deleted file mode 100644 index 9fd018e..0000000 --- a/b.txt +++ /dev/null @@ -1 +0,0 @@ -version 1 of b.txt [root@cscs-100-116-20-141 my_project]# 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) # # modified: a.txt # deleted: b.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # c.txt # d.txt no changes added to commit (use "git add" and/or "git commit -a")
(3) git reset --hard
恢复到进行 git reset 实验前的状态!
执行 git reset --hard HEAD^
后,Git 先执行了 reset 的第一个步骤(同时移动 HEAD 与它指向的分支到前一个提交节点),然后执行第二个步骤(用 HEAD 指向的当前快照的内容来 覆盖
到暂存区),最后执行第三个步骤(用 HEAD 指向的当前快照的内容来 覆盖
到工作区中已被 Git 追踪管理的文件),即它本质上是 撤销了上一次提交
,并取消 暂存区
和 工作区
的所有内容
危险: 会丢失工作区中所有已被 Git 追踪管理但是新变更尚未提交(比如 a.txt 的 v4 版本永久丢失)的内容!
[root@cscs-100-116-20-141 my_project]# git reset --hard HEAD^ HEAD is now at c32d77f Modified a.txt(v2); add b.txt(v1) # 1.HEAD 和 master 指向的提交节点变了 [root@cscs-100-116-20-141 my_project]# git log --all --graph --pretty=oneline --abbrev-commit --decorate=short * c32d77f (HEAD, master) Modified a.txt(v2); add b.txt(v1) * 90471f3 Add a.txt(v1) * d9e659d (develop, bugFix3, bugFix) renamed: README -> README.md * 8bd7143 Add C2.txt | * 6c586a3 (bugFix2) bug fix 02 |/ * 3174178 Add C1.txt; modify README * 1d36d90 init repo # 2.暂存区被覆盖,它与 HEAD 现在没有差异 [root@cscs-100-116-20-141 my_project]# git diff --cached # 3.工作区被覆盖,它与暂存区现在没有差异 [root@cscs-100-116-20-141 my_project]# git diff # d.txt 这种 Untracked files 没有影响 [root@cscs-100-116-20-141 my_project]# git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # d.txt nothing added to commit but untracked files present (use "git add" to track)
2.2 文件级别的重置
我们可以提供 路径(paths)
来重置指定文件,跟提交级别的重置不同的是,若指定了一个路径,将会跳过 reset 步骤 1
(不会移动 HEAD 与它指向的分支),并且将它的作用范围限定为指定的文件或文件集合。因为 HEAD
只是一个指针,你无法让它同时指向两个提交中各自的一部分。不过暂存区和工作区可以部分更新,所以会继续进行 reset 步骤 2
/reset 步骤 3
注意: Cannot do soft reset with paths. / Cannot do hard reset with paths.
(1) 取消暂存文件
# 将一个或多个文件从暂存区中移除,用 HEAD 指向的快照中的对应文件覆盖到暂存区;而工作区的内容不变。本质上只是将 <file>... 从 HEAD 复制到暂存区中 # 它是 git reset --mixed HEAD <file>... 的简写形式,因为你既没有指定一个提交的 SHA-1 或分支名 # 还可以简写为 git reset <file>... git reset HEAD <file>... # 将一个或多个文件从暂存区中移除,通过具体指定一个提交(90471f3)或分支来拉取该文件的对应版本;而工作区的内容不变 git reset 90471f3 <file>... git reset master <file>...
恢复到进行 git reset 实验前的状态!
我们执行 git reset 90471f3 a.txt
来取消暂存区中 a.txt,并替换为提交(90471f3)中的版本:
# 重置 a.txt 前,HEAD 指向的快照中 a.txt 内容为 version 3 of a.txt [root@cscs-100-116-20-141 my_project]# git ls-tree -r HEAD 100644 blob 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 README.md 100644 blob 8775fe8be49c3a4b67b8106fea615eb5ab50591a a.txt 100644 blob 69ef72f1f278f34d8a8e9c469ccf2196d8451bab c.txt 120000 blob 42061c01a1c70097d1e4579f29a5adf40abdec95 help.txt 100644 blob b1b716105590454bfc4c0247f193a04088f39c7f init.txt 100644 blob a258e13b63ae84eda30d4d4fc940dc58159e32f7 utils/C1.txt 100644 blob 18b5e4c8c8a5568eb522f1599714946678c2d8bd utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 8775fe8 version 3 of a.txt # 重置 a.txt 前,暂存区中 a.txt 内容为 version 4 of a.txt,因为工作区中修改了 a.txt 并执行 git add 了 [root@cscs-100-116-20-141 my_project]# git ls-files -s 100644 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 0 README.md 100644 e0b4d4bdbc6e45d092a2df4942f6863c781a0527 0 a.txt 100644 69ef72f1f278f34d8a8e9c469ccf2196d8451bab 0 c.txt 120000 42061c01a1c70097d1e4579f29a5adf40abdec95 0 help.txt 100644 b1b716105590454bfc4c0247f193a04088f39c7f 0 init.txt 100644 a258e13b63ae84eda30d4d4fc940dc58159e32f7 0 utils/C1.txt 100644 18b5e4c8c8a5568eb522f1599714946678c2d8bd 0 utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p e0b4d4b version 4 of a.txt [root@cscs-100-116-20-141 my_project]# cat a.txt version 4 of a.txt # 从提交(90471f3)中拉取 a.txt 的对应版本,覆盖到暂存区 [root@cscs-100-116-20-141 my_project]# git reset 90471f3 a.txt Unstaged changes after reset: M a.txt M c.txt # 重置 a.txt 后,暂存区中 a.txt 内容为 version 1 of a.txt,将 90471f3 提交中的 a.txt 复制到了暂存区。c.txt 还存在,说明只单独恢复了 a.txt,而不是将 90471f3 整个提交覆盖到暂存区 [root@cscs-100-116-20-141 my_project]# git ls-files -s 100644 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 0 README.md 100644 0fdd823d5eae387324c523ade942d5e3b19f2993 0 a.txt 100644 69ef72f1f278f34d8a8e9c469ccf2196d8451bab 0 c.txt 120000 42061c01a1c70097d1e4579f29a5adf40abdec95 0 help.txt 100644 b1b716105590454bfc4c0247f193a04088f39c7f 0 init.txt 100644 a258e13b63ae84eda30d4d4fc940dc58159e32f7 0 utils/C1.txt 100644 18b5e4c8c8a5568eb522f1599714946678c2d8bd 0 utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 0fdd823 version 1 of a.txt # 重置 a.txt 后,工作区的 a.txt 内容不受影响 [root@cscs-100-116-20-141 my_project]# cat a.txt version 4 of a.txt # 重置 a.txt 后,HEAD 指向的快照中 a.txt 内容还是 version 3 of a.txt,即没有移动 HEAD 与它指向的分支 [root@cscs-100-116-20-141 my_project]# git ls-tree -r HEAD 100644 blob 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 README.md 100644 blob 8775fe8be49c3a4b67b8106fea615eb5ab50591a a.txt 100644 blob 69ef72f1f278f34d8a8e9c469ccf2196d8451bab c.txt 120000 blob 42061c01a1c70097d1e4579f29a5adf40abdec95 help.txt 100644 blob b1b716105590454bfc4c0247f193a04088f39c7f init.txt 100644 blob a258e13b63ae84eda30d4d4fc940dc58159e32f7 utils/C1.txt 100644 blob 18b5e4c8c8a5568eb522f1599714946678c2d8bd utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 8775fe8 version 3 of a.txt
3、git checkout
3.1 提交级别的检出
运行 git checkout <branch>/<commit>
与运行 git reset --hard <branch>/<commit>
非常相似,它会更新所有三棵树使其看起来像 <branch>/<commit>
,不过有两点重要的区别:
git checkout
只会移动HEAD
本身、不移动原分支。git reset --hard
会同时移动 HEAD 与它指向的分支git checkout
对工作目录是安全的,如果检查到工作区有修改内容还未提交,Git 会阻止检出。而git reset --hard
则会不做检查就全面覆盖所有已追踪的文件
恢复到进行 git reset 实验前的状态!
因为工作区有修改内容未提交,所以 Git 会阻止检出:
[root@cscs-100-116-20-141 my_project]# git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: a.txt # # 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: c.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # d.txt [root@cscs-100-116-20-141 my_project]# git checkout 90471f3 error: Your local changes to the following files would be overwritten by checkout: a.txt Please, commit your changes or stash them before you can switch branches. error: Your local changes to the following files would be overwritten by checkout: c.txt Please, commit your changes or stash them before you can switch branches. Aborting
执行 git stash
命令将工作区变更内容存起来后,可以检出到 90471f3:
[root@cscs-100-116-20-141 my_project]# git checkout 90471f3 Note: checking out '90471f3'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at 90471f3... Add a.txt(v1) [root@cscs-100-116-20-141 my_project]# git status # HEAD detached at 90471f3 # Untracked files: # (use "git add <file>..." to include in what will be committed) # # d.txt nothing added to commit but untracked files present (use "git add" to track) [root@cscs-100-116-20-141 my_project]# git diff [root@cscs-100-116-20-141 my_project]# git diff --cached [root@cscs-100-116-20-141 my_project]# tree . ├── a.txt ├── d.txt ├── help.txt -> README.md ├── init.txt ├── README.md └── utils ├── C1.txt └── C2.txt 1 directory, 7 files [root@cscs-100-116-20-141 my_project]# cat a.txt version 1 of a.txt
3.2 文件级别的检出
(1) 撤消对文件的修改
运行 git checkout HEAD/<branch>/<commit> <file>...
会像 git reset HEAD/<branch>/<commit> <file>...
那样用对应提交中的那个文件来更新暂存区,同时它也会覆盖工作区中对应的文件,所以该命令对工作区并不安全。另外,它也不会移动 HEAD
危险: 会丢失工作区中所有已被 Git 追踪管理但是新变更尚未提交(比如 a.txt 的 v4 版本永久丢失)的内容!
恢复到进行 git reset 实验前的状态!
# 检出 a.txt 前,HEAD 指向的快照中 a.txt 内容为 version 3 of a.txt [root@cscs-100-116-20-141 my_project]# git ls-tree -r HEAD 100644 blob 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 README.md 100644 blob 8775fe8be49c3a4b67b8106fea615eb5ab50591a a.txt 100644 blob 69ef72f1f278f34d8a8e9c469ccf2196d8451bab c.txt 120000 blob 42061c01a1c70097d1e4579f29a5adf40abdec95 help.txt 100644 blob b1b716105590454bfc4c0247f193a04088f39c7f init.txt 100644 blob a258e13b63ae84eda30d4d4fc940dc58159e32f7 utils/C1.txt 100644 blob 18b5e4c8c8a5568eb522f1599714946678c2d8bd utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 8775fe8 version 3 of a.txt # 检出 a.txt 前,暂存区中 a.txt 内容为 version 4 of a.txt,因为工作区中修改了 a.txt 并执行 git add 了 [root@cscs-100-116-20-141 my_project]# git ls-files -s 100644 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 0 README.md 100644 e0b4d4bdbc6e45d092a2df4942f6863c781a0527 0 a.txt 100644 69ef72f1f278f34d8a8e9c469ccf2196d8451bab 0 c.txt 120000 42061c01a1c70097d1e4579f29a5adf40abdec95 0 help.txt 100644 b1b716105590454bfc4c0247f193a04088f39c7f 0 init.txt 100644 a258e13b63ae84eda30d4d4fc940dc58159e32f7 0 utils/C1.txt 100644 18b5e4c8c8a5568eb522f1599714946678c2d8bd 0 utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p e0b4d4b version 4 of a.txt [root@cscs-100-116-20-141 my_project]# cat a.txt version 4 of a.txt # 从提交(90471f3)中拉取 a.txt 的对应版本,覆盖到暂存区、工作区 [root@cscs-100-116-20-141 my_project]# git checkout 90471f3 a.txt # 检出 a.txt 后,暂存区中 a.txt 内容为 version 1 of a.txt,将 90471f3 提交中的 a.txt 复制到了暂存区。c.txt 还存在,说明只单独恢复了 a.txt,而不是将 90471f3 整个提交覆盖到暂存区 [root@cscs-100-116-20-141 my_project]# git ls-files -s 100644 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 0 README.md 100644 0fdd823d5eae387324c523ade942d5e3b19f2993 0 a.txt 100644 69ef72f1f278f34d8a8e9c469ccf2196d8451bab 0 c.txt 120000 42061c01a1c70097d1e4579f29a5adf40abdec95 0 help.txt 100644 b1b716105590454bfc4c0247f193a04088f39c7f 0 init.txt 100644 a258e13b63ae84eda30d4d4fc940dc58159e32f7 0 utils/C1.txt 100644 18b5e4c8c8a5568eb522f1599714946678c2d8bd 0 utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 0fdd823 version 1 of a.txt [root@cscs-100-116-20-141 my_project]# cat c.txt version 2 of c.txt # 检出 a.txt 后,工作区的 a.txt 内容也被覆盖 [root@cscs-100-116-20-141 my_project]# cat a.txt version 1 of a.txt # 检出 a.txt 后,HEAD 指向的快照中 a.txt 内容还是 version 3 of a.txt,即没有移动 HEAD 与它指向的分支 [root@cscs-100-116-20-141 my_project]# git ls-tree -r HEAD 100644 blob 5e1b2cf75e9102b1e84c26d4c7bf2155fe6db005 README.md 100644 blob 8775fe8be49c3a4b67b8106fea615eb5ab50591a a.txt 100644 blob 69ef72f1f278f34d8a8e9c469ccf2196d8451bab c.txt 120000 blob 42061c01a1c70097d1e4579f29a5adf40abdec95 help.txt 100644 blob b1b716105590454bfc4c0247f193a04088f39c7f init.txt 100644 blob a258e13b63ae84eda30d4d4fc940dc58159e32f7 utils/C1.txt 100644 blob 18b5e4c8c8a5568eb522f1599714946678c2d8bd utils/C2.txt [root@cscs-100-116-20-141 my_project]# git cat-file -p 8775fe8 version 3 of a.txt
4. 总结
是否移动 HEAD 或分支 | 是否复制到暂存区 | 是否复制到工作区 | 工作区是否安全 | |
---|---|---|---|---|
提交级别 | ||||
git reset --soft <commit> |
HEAD+分支 | 否 | 否 | 是 |
git reset [--mixed] <commit> |
HEAD+分支 | 是 | 否 | 是 |
git reset --hard <commit> |
HEAD+分支 | 是 | 是 | 否 |
git checkout <commit> |
HEAD | 是 | 是 | 是 |
文件级别 | ||||
git reset <commit> <file>... |
否 | 是 | 否 | 是 |
git checkout <commit> <file>... |
否 | 是 | 是 | 否 |
0 条评论
评论者的用户名
评论时间暂时还没有评论.