git重置--仅软更改哈希值吗?

git重置--仅软更改哈希值吗?,git,git-reset,Git,Git Reset,在这篇文章中,作者很好地解释了git重置软、混合和硬的3个选项: 他使用这三棵树作为设备来表示1个工作树、2个临时区域、3个提交历史记录/提交引用: -硬复位1,2,3 -混合复位2和3 -软只改变3 我不太清楚3到底代表了什么。我可以看到如何使用git reset-soft来更改提交分支指向的对象。但我不知道为什么这里用历史这个词。除了分支和头都引用的提交之外,还修改了什么 编辑:特别是git reset-soft是否只编辑i.git/refs/heads/master文件中的散列值,ii.

在这篇文章中,作者很好地解释了git重置软、混合和硬的3个选项:

他使用这三棵树作为设备来表示1个工作树、2个临时区域、3个提交历史记录/提交引用:

-硬复位1,2,3

-混合复位2和3

-软只改变3

我不太清楚3到底代表了什么。我可以看到如何使用git reset-soft来更改提交分支指向的对象。但我不知道为什么这里用历史这个词。除了分支和头都引用的提交之外,还修改了什么

编辑:特别是git reset-soft是否只编辑i.git/refs/heads/master文件中的散列值,ii.git/HEAD文件中的散列值,而不编辑其他内容

正如作者在主题中所写,要理解这一点,您需要 理解git内部结构

我会尽量用最好的方式来解释

在git提交中,是引用tree的链表,它引用Blob文件和树

>    C1<------C2<--------C3
>     |        |          |
>     V        V          V
>     T1       T2         T3
>     |       / \         /\
>     V      /   v       /  \
>     B1 <--     B2 <---    B1'
正如你在上面看到的

在提交1中,即C1有一个文件B1 在C2中,添加了一个新文件B2。 在C3中,B1文件更改为B1',并在数据结构中添加完整文件的新快照。 我上面解释的是git的内部结构

GIT使用DAG有向无环图数据结构

现在,分支、重置和签出命令只能在提交级别工作,因为您可以看到提交形成一个链表

所以假设您的分支指向提交C2,现在您在同一分支中添加了一个新的提交C3,那么分支指针将从C2移动到C3

类似地,reset和commit相反,所以当您执行reset时,指针将从当前提交移到back commit。 假设您在提交C2上,并且确实重置了,则将当前分支指针移动到上一次提交

让我们来谈谈软,硬和混合。重置有3个选项

硬:这里指针被移动到上一次提交,并且上一次提交的更改被从工作目录中完全删除

混合:此处指针移动到上一次提交,并且在工作目录中维护对上一次提交的更改,而无需转移/添加它们,即在运行命令的情况下

git复位-混合头~1

git提交

没什么要承诺的,正在清理树

当您需要使用添加/暂存文件时

软:在这里,指针被移动到上一次提交,上一次提交的更改被保存在工作目录中,并添加了它们,即在您运行命令的情况下 git复位-混合头~1

git提交

它将创建一个新的提交,因为所有更改都是为了提交而进行的


如果您有任何问题,请告诉我

既然您询问的是具体的实现,我认为这实际上更容易解释,那么看看.git/HEAD中的实际内容,当您在分支上时:

$ cat .git/HEAD
ref: refs/heads/master
$ git checkout -b new
Switched to a new branch 'new'
$ cat .git/HEAD
ref: refs/heads/new
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ cat .git/HEAD
ref: refs/heads/master
所以,只要我在一个分支上,git状态就会说,分支的名称,引用的全名,实际上在.git/HEAD中。因此,git/HEAD不会改变,也不需要改变

.git/refs/heads/master是否存在的问题更大:

$ cat .git/refs/heads/master
cat: .git/refs/heads/master: No such file or directory
$ git rev-parse master
b5101f929789889c2e536d915698f58d5c5c6b7a
$ grep master .git/packed-refs
b5101f929789889c2e536d915698f58d5c5c6b7a refs/heads/master
b5101f929789889c2e536d915698f58d5c5c6b7a refs/remotes/origin/master
这里发生的事情是Git打包了我的ref名称,因此不再存在普通文件:refs/heads/master存储在.Git/packed refs中,作为几行中的一行,在本例中另一个匹配行是refs/remotes/origin/master

也就是说,对散列ID映射的引用存储在某个数据库中,而不一定存储在一个简单的文件中。不过,打包的refs数据库仍然非常简单

不过,要回答您的最终问题,答案是肯定的:git reset-soft将写入名称到散列ID映射。即使我们使用名称而不是散列ID,这也是正确的:

$ git checkout new
$ git reset --soft master~3
名称new现在指的是名称master~3所指的相同提交哈希ID:

$ git rev-parse new
371820d5f1bb3c3e691ad21cee652c02c36ea758
$ git rev-parse master~3
371820d5f1bb3c3e691ad21cee652c02c36ea758
在当前版本的Git中,在名称new中写入新哈希ID的行为会通过写入简单文件.Git/refs/heads/new来覆盖压缩引用的数据库,但您不应该依赖于此,而是使用Git rev parse和Git update ref

自从我在上面的master中创建了new之后,这仅仅起到了将new名称移回三个第一父级hops master~3的效果。这意味着new是master的祖先,所以:

。。。所以Git现在可以简单地删除新名称,因为它完全合并到master

但我不知道为什么这里用历史这个词

历史并不是最好的字眼。要真正理解这一点,请通读网站。这里的关键概念是可达性。更改存储在分支名称下的提交散列ID(就像git reset所做的那样)会更改可访问的提交集。如果集合增长,则可以访问更多的提交;如果它收缩,则可访问的提交数减少;如果保持相同的大小,则可以访问相同数量的提交,但集合本身可能相同,也可能不同


粗略地说,故事是存储库中的提交集,或者从某个名称可以访问的提交集,或者从某个名称可以访问的提交子集。使用一些但不是所有这些松散的定义,移动名称会改变历史。

谢谢您的回答。顺便说一句,我认为可以对它进行重新编写,使它更清楚,在这里您谈到git reset将分支带回一个commit,因为只有在您提供HEAD~参数的情况下才会出现这种情况,不是吗?我想在我的问题中传达的是:git reset-soft是否只编辑.git/refs/heads/master文件中的散列值和.git/HEAD中的值?我认为.git/HEAD甚至不会被修改,因为它指向分支,而不是直接指向提交。至少在我刚做的几次测试中没有。
$ git rev-parse new
371820d5f1bb3c3e691ad21cee652c02c36ea758
$ git rev-parse master~3
371820d5f1bb3c3e691ad21cee652c02c36ea758
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ git branch -d new
Deleted branch new (was 371820d5f1).