我使用git worktree add
创建了一个工作树,并且工作了几天。今天,我想在以前的版本中检出一些代码,所以我暂时分离了HEAD
:
git checkout head^
找到所需的内容后,我尝试回到工作树分支的顶端:
git checkout <branchname>
但是现在,HEAD
指向我的本地master
分支中的最高提交。例如:
git show head
显示master
中的最新提交。
有什么想法为什么会发生或如何解决?
编辑
我也看到一些奇怪的,不一致的行为。例如,这:
> git reset --hard
HEAD is now at 4f1666467017
> git rev-parse head
e4fab17872cce2f6f37f5b596c7ed2739b135f3f
答案 0 :(得分:0)
(注意:其中一些是通用的。)
这里额外的怪异可能与每个工作树都有自己的专用HEAD
文件这一事实有关。不同的操作可能会通过大小写折叠找到不同的HEAD
文件:添加的工作树有一个.git/worktrees/worktree/HEAD
文件,而主工作树有一个.git/HEAD
文件。如果操作 A 选择.git/HEAD
来匹配head
,而操作 B 选择.git/worktrees/worktree/HEAD
,则您会看到这种情况。使用全键盘HEAD
应该使Git做正确的事。
(特别是git reset --hard
在所有大写内部使用HEAD
,这是指每个工作树HEAD
,而git rev-parse head
使用小写的{{1 }},它将引用head
文件。)
您可以通过其原始哈希ID来查看任何历史提交:
.git/HEAD
结果就是Git所说的分离头。通常,特殊名称git checkout a123456
(用大写字母表示)是附加,并附加到分支名称:
HEAD
(此处的大写字母代表实际的提交哈希ID)。通过像这样附加,...--A--B--C--D <-- master (HEAD)
\
E--F--G <-- branch
意味着HEAD
,这意味着提交master
。因此,您当前的提交现在就是任何提交D
的真实哈希ID。如果您运行:
D
您得到:
git checkout branch
,这意味着您当前的分支为...--A--B--C--D <-- master
\
E--F--G <-- branch (HEAD)
,而您当前的 commit 现在为branch
。
但是,当您通过哈希ID选择提交时(例如,提交G
,则使E
指向该提交:
HEAD
这就是Git所说的“分离头”;您当前的提交是提交...--A--B--C--D <-- master
\
E <-- HEAD
\
F--G <-- branch
。
现在,您没有使用原始哈希ID。相反,您键入了E
。但是有很多命名提交的方法。原始哈希ID是最低级别的方法:哈希ID始终有效,并且始终表示一个特定的提交,但是您也可以让Git查找某个提交的 parent 。这就是head^
的含义:选择当前提交的父级。假设当前提交为HEAD^
,因为您在G
上;然后branch
个名称提交HEAD^
,之后:
F
您将拥有:
git checkout HEAD^
小写版本...--A--B--C--D <-- master
\
E--F <-- HEAD
\
G <-- branch
在Windows和MacOS上有效,但在Linux上无效。这是以下事实的副作用:Git有时会将这些内容存储在文件中(例如head
和.git/HEAD
)。使用.git/refs/heads/master
而非head
,Git可能会尝试打开不存在的HEAD
,但是当Git尝试打开.git/refs/heads/head
时,它会得到.git/head
,确实存在。因此,在这些计算机上的 中,.git/HEAD
与git checkout head^
的含义相同:找到当前的提交,退回到其第一个父提交,并检查那个提交。
要将git checkout HEAD^
重新附加到某个分支,请使用HEAD
。
如果您不在Windows或MacOS上(即使您不在),请注意,您也可以 创建一个名为git checkout branch-name
的分支或标签 。如果这样做,事情会变得有些混乱,尤其是在Windows和MacOS上,因为现在有两种 解析方式head
:分支或标记,以及head
。最好不要这样做。