在这篇文章中,作者很好地解释了git reset的3个选项(软,混合,硬): https://www.atlassian.com/git/tutorials/undoing-changes/git-reset
他使用“三棵树”作为代表1)工作树,2)临时区域,3)“提交历史记录/提交引用”的设备:
--hard
重设(1),(2),(3);
--mixed
重置(2)和(3);
--soft
仅更改(3)。
这是(3)实际代表的意思,我不清楚。我可以看到如何使用git reset --soft
来更改分支指向哪个提交。但是我不知道为什么在这里使用历史这个词。除了分支和HEAD都引用的提交以外,究竟做了哪些修改?
编辑:特别是git reset --soft <SHA1>
仅 编辑i).git/refs/heads/master
文件中的哈希值和ii).git/HEAD
内部的哈希值,而没有其他内容吗?
答案 0 :(得分:3)
由于您正在询问具体的实现方式(我认为实际上还是更容易解释),因此在分支机构时,请查看.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 status
会说“在树枝上”,树枝的名称(实际上就是引用的全名)就位于{ {1}}。因此.git/HEAD
不会改变,也不需要改变。
.git/HEAD
是否存在还存在更多问题:
.git/refs/heads/master
这里发生的是Git已经打包了我的引用名称,因此不再有一个普通文件:$ 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
被存储在refs/heads/master
中,作为以下几个文件之一行(在这种情况下,另一条匹配行是.git/packed-refs
)。
也就是说,对哈希ID的引用映射存储在某些数据库中,不一定存储在简单文件中。 (不过,打包引用的“数据库”仍然非常简单。)
不过,要回答您的最终问题,只是是:refs/remotes/origin/master
将git reset --soft <hash>
写入名称到哈希ID的映射中。即使我们使用名称而不是哈希ID,也是如此:
<hash>
名称$ git checkout new
$ git reset --soft master~3
现在与名称new
所引用的提交哈希ID相同:
master~3
(在当前版本的Git中,将新的哈希ID写入名称$ git rev-parse new
371820d5f1bb3c3e691ad21cee652c02c36ea758
$ git rev-parse master~3
371820d5f1bb3c3e691ad21cee652c02c36ea758
的行为是通过编写简单文件new
来覆盖打包引用的数据库,但是您不应这样做取决于此,请改用.git/refs/heads/new
和git rev-parse
。)
自从我从上方的git update-ref
创建new
以来,这只产生了将名称master
移回三个第一亲跳(new
)的效果。这意味着master~3
是new
的祖先,所以:
master
......这样Git就可以了,只需立即删除名称$ 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).
,因为它已完全合并到new
。
但是我不知道为什么在这里使用历史这个词。
“历史”并不是最好的词。要真正理解这一点,请阅读网站Think Like (a) Git。这里的关键概念是可达性。像master
一样,更改存储在分支名称下的提交哈希ID,会更改可到达的提交集。如果集合增加,则可以达到更多提交;如果缩小,则可到达的提交更少;如果保持相同的大小,则可以达到相同的数量个提交,但是集合本身可以相同也可以不相同。
从松散和模糊的角度来说,“历史记录”是存储库中的一组提交,或者某些名称可以到达的一组提交,或者某些名称可以到达的某些提交子集。使用一些但不是全部这些松散的定义,移动名称会更改历史记录。
答案 1 :(得分:1)
正如作者在主题中所写,为了理解这一点,您需要 了解git内部原理。
我会尽力解释。
在git commit中,是引用树的链接列表,其中引用了blob(文件)和树。
> C1<------C2<--------C3
> | | |
> V V V
> T1 T2 T3
> | / \ /\
> V / v / \
> B1 <-- B2 <--- B1'
如您所见
我上面解释的是git的内部结构。
GIT使用DAG(有向无环图数据结构)
现在,分支,重置和签出命令仅在提交级别上起作用(如您所见,提交来自链接列表)。
因此,假设您的分支指向C2提交,现在您在同一分支中添加了一个新的C3,因此分支指针将从C2移到C3。
类似地,重置与提交相反,因此当您执行重置时,指针将移动到从当前提交向后的提交。 假设您正在提交C2,并且要进行重置,则将当前分支指针移至上一个提交。
让它们变软,变硬并混合。重置有3个选项
hard:这里的指针移至上一次提交,并且先前提交的更改已从工作目录中完全删除。
已混合:此处将指针移至上一个提交,并且对上一个提交的更改将保留在工作目录中,而无需暂存/添加它们,即在运行命令的情况下
git reset --mixed HEAD〜1
git commit
什么也没提交,工作树很干净
因为您需要使用
git add <filename>
git reset --mixed HEAD〜1
git commit
由于所有更改都已提交,因此它将创建一个新的提交。
如果您有任何疑问,请告诉我。 :)