假设我有一个git仓库,正在使用一些分支,并且在某个时刻我想要没有任何分支被检出。我能做到吗?
换句话说,我能否在现有的仓库中获得git clone path-to-repo --no-checkout
的效果?
编辑:实际上,如果主分支文件不会显示为删除,那将会更好。所以,理想的答案不应该完全像带有--no-checkout
的git clone
。
注:如果您的建议需要一个较新版本的git,请说明。
git rm -r
来删除所有文件,也可能需要git clean
。还有一种特殊情况,您可以什么都不做,也许您真的不想删除文件。
在这种类型的仓库中,你只有一种选择,那就是使用“孤儿分支”。我们马上会回到这个话题。
否则,在有提交时,你可以拥有任意数量的有效、已存在的分支名称,每个分支名称必须指向某个已存在的有效提交。你可以在其中一个这些分支名称上,或者处于断头状态,或者使用“孤儿分支”模式。孤儿模式: HEAD
包含一个分支名称。这是当前的分支,所以你在一个分支上。奇怪的是,你所在的分支实际上是不存在的。执行 git branch
命令会列出存在的分支名称,但不会列出你所在的分支名称,因为它不存在。
正常模式: HEAD
包含一个分支名称。那就是当前的分支,存储在该分支名称中的提交哈希值是当前提交。
分离 HEAD 模式: HEAD
包含一个提交哈希值。那就是当前的提交;没有当前的分支。
git clone
命令通常会进入正常模式。成为当前分支的分支名称是您在 git clone
时选择的,使用您的 -b
选项。但是,有一些例外情况:如果您的 -b
指定了一个标签(tag),Git 将检出该标签,将您置于分离头状态。如果您没有指定 -b
选项,则您的 Git 会询问其他 Git 推荐的分支名称,并使用该名称或备用名称;如果该名称或备用名称未能命名分支,则您将进入孤立模式,当前分支为不存在的分支。如果您指定了 -b
选项,则名称必须命名另一个 Git 存储库中的现有分支或标签,否则整个克隆命令将失败。
-n
选项对新克隆的模式没有影响,您将像没有使用该选项一样处于普通、分离的HEAD或孤立的模式。 -n
选项的唯一影响是它防止了初始的git checkout
。在使用正常模式时,分支名称仍然会在本地创建,因此,如果您克隆的存储库中有分支,您将在其中一个分支上,并且该分支名称在本地存在并指向与远程跟踪名称相同的提交。这是一个奇怪的特殊情况,我认为这是一个小错误,因为如果您运行了git init
、git remote add
和git fetch
而没有执行git checkout
,则会被留在孤立模式中。(除了git clone
本身之外的命令,创建分支的步骤是git checkout
,因此跳过它应该会使您留在孤立模式中。但事实并非如此。)
1但前提是,你必须花足够的计算时间来产生哈希冲突。请参见 新发现的SHA-1碰撞如何影响Git?。请注意,这不是偶然发生的,对于大多数团体而言,即使是有意为之,也是不切实际的(虽然像谷歌这样的大公司以及各个国家机构已经具备了这种能力)。
git clone -n
有何关系?当使用git clone -n
时,会得到HEAD
的三种模式之一:孤立分支、分离头指针或正常模式。但Git不会运行git checkout
,而是git checkout
会填充Git的索引和您的工作树。因此,您将拥有一个名义上为空的索引和工作树。2
因此,如果您希望精确地再现这个条件,则需要:
HEAD
设置;以及git read-tree --empty
,它会清除索引,然后使用各种选项的git clean
。您可以使用git read-tree --empty -u
来删除所有已索引的文件,在工作树中仅保留未跟踪的文件。或者您可以选择保持工作树不变。git checkout --detach
或git checkout
,但不是分支名称,或者git switch --detach
。git checkout --detach
而没有参数时的HEAD
提交)成为当前提交,您现在处于分离头状态。您在此处检出的提交(或使用git switch
切换到的提交)将填充Git的索引并更新工作树中的文件,但除了在当前分支有未提交的更改时检出另一个分支中概述的特殊情况外。git checkout --orphan
或git switch --orphan
。请注意这种隐蔽的不兼容性:旧的检出方法保留Git的索引和您的工作树不受干扰。git switch
命令会清空索引并像使用git read-tree --empty -u
一样清理您的工作树。2一个空的索引是一个非零长度的文件,因为索引具有头部和尾部。它们包含加密哈希,以便检测磁盘上的损坏,就像存储库对象数据库一样。为了方便起见,Git将不存在的索引视为“空”,并在内存中创建空索引,并在适当时候用正确的校验和写入。
工作树的顶层通常包含.git
目录,这是存储库本身,因此“空”工作树永远不会完全为空。但是,您可以使用各种选项将存储库和工作树目录分开。
无论你选择哪种模式,注意运行git commit
将会像往常一样尝试创建新的提交:
在孤立模式下,这将创建一个没有父级(即一个新的根提交)的新提交,并创建其名称在HEAD
中的分支。该分支现在存在,并持有一个根提交,不通过提交图连接到任何其他提交。
(在合并结束时执行此操作可能是个坏主意。我不知道如果你尝试这样做会发生什么。)
在脱离HEAD模式下,这将创建一个带有通常父级(当前提交作为父级,加上正在进行的合并的任何其他提交)的提交。Git将在HEAD
中存储新提交的哈希ID,它仍然是脱离的,但现在指向一个只能通过HEAD
找到的提交。
在正常模式下,这将像通常一样创建新的提交(包括像脱离HEAD模式一样结束合并),然后将新提交的哈希ID存储在HEAD
中存储的分支名称中。
git commit
一个新的空树。其中最简单的可能是分离的HEAD模式;这样,您可以不太担心地清除索引和工作树,也可以不清除。是的,删除这些文件。
git clone --no-checkout
仍然在一个分支上,只是没有文件。
git clone --no-checkout
的确切效果时,我说错了。请看澄清的编辑。 - einpoklum
git status
就不会抱怨任何东西被删除了;并且没有分支是当前的。git checkout --orphan
时,至少在我的git版本(2.20.1)中需要为孤立分支命名。git switch --orphan <name>
(或者使用git checkout
但需要使用git rm
或git read-tree --empty
技巧)。请注意,git switch
是在2.23版本中新增的。 - torekgit checkout --detach ...
对我有用。 - Meir Gabay