这个问题 - 或基本相似的“如何撤消git fetch
” - 之前曾被问过几次,但答案总是过于简单化。特别是,最流行的答案(git reset --hard
)根本不会触及存储库。
我正在寻找的答案应该留下本地git repo(不是工作目录,而不是索引;我真的不关心那些最终处于什么状态)处于同一状态(bit-for-bit,理想情况下,它是在取之前。我会接受重新克隆上游或修改本地回购。我可以使用忽略多个分支,多个遥控器等的解决方案,但是一个更成熟的解决方案会很好。
我一直在使用的测试是:
git show
显示$want
git log
从$want
git show $latest
错误git tag -l $latesttagname
没有显示任何内容git show $latesttag
错误git fetch -v
清楚地下载了我剥离的所有东西(它应该不说“最新”)其中$latest
是我尝试撤消的提取后头部的提交哈希值,$latesttag
是需要剥离的提交集中最新标记提交的提交哈希值,$latesttagname
是该标记的名称,$want
是旧头部的提交哈希值,一旦剥离其后代,我想成为新头部。
我尝试过的事情:
git reset --hard $want
:仅通过前两个测试
添加'git prune -nv'没有帮助
git gc --prune=now
如果我在执行reset
后克隆了回购,那么在孩子中第三次测试通过,第六次出现(在将远程URL设置到适当的上游之后)
通过所有测试的原因是在克隆之前在我的本地父母中运行git tag -d $(git tag --contains $want | tail +2)
;特别是,之前由标签引用的提交不再被引用,所以我猜他们会被抛在脑后。但它表明我可能忽略了其他参考类型。
我带出了大锤子,做了一些似乎接近hg strip
所做的事情(为那里的zshisms道歉):
tags=( $(git tag --contains $want | tail +2) )
mv .git/objects/pack .
git unpack-objects < pack/pack*.pack
rm -rf pack
git log --pretty=format:%H $want..HEAD | while read i; do rm .git/objects/$i[1,2]/$i[3,-1]; done
git update-ref refs/remotes/origin/master $want
git update-ref refs/heads/master $want
for i in refs/tags/$^tags; do git update-ref -d $i; done
git repack -ad
几乎完成了你期望锤子做的事情 - 打破一堆事情:git fsck --unreachable
抱怨一堆无效的reflog条目和无法访问的blob(和一个悬空提交),但克隆使得这些消失了。
在我看来,reset
+标记删除+ clone
是我在这里最好的选择,但是a)我需要删除/重新命名的其他参考,以及b)是否存在一种避免需要克隆的方法?还是完全有另一种方式?
道歉的长度。我希望尽可能清楚地了解我正在寻找的东西(以及我没有找到的东西)以及我已经发现的失败(以及为什么)。如果你走得这么远,谢谢你。
答案 0 :(得分:0)
TL; DR回答:你的重置,删除任何指向重置区域的标签,克隆方法有效,可能就是这样。
如果你想清理一个现有的仓库,你(至少可能)需要git for-each-ref
和reflog清理(每个ref的reflog,然后是HEAD
)
在没有克隆的情况下可以(但显然更难)做到这一点,并且您正在使用git gc --prune=now
(或--prune=all
正确的轨道,正如文档所述;尽管它可能更好地使用显式git repack
和git prune
,您可能还需要清除一些reflog条目。
&#34;大锤子&#34;方法可能过于激进,因为它可以 2 删除所需的对象,因为它们与您保留的提交中的对象相同。 (在意识到你只是删除提交对象之后编辑:并且,它可能会留下对象,尽管它们在正常使用中不会被看到。这是否是一个大问题取决于删除这些对象的重要性。请参阅下面的一般理论部分。)
克隆更容易,因为克隆只会带来从它带来的引用中可以访问的那些对象。也就是说,您可以控制添加的内容,而不是必须避免的内容。只要您使用智能协议和/或展开任何包(例如,请参阅下面的包部分),您的方法(删除任何剩余标签并确保没有分支包含您想要的任何提交)就足够了。
有点形式上,假设整个存储库图是 G
(这是存储在存储库中的所有对象提交,标记,树和blob)。克隆通过从克隆的分支名称开始查找 g
的子集 G
, 1 查找对象这些名称,然后递归添加从这些对象可到达的任何对象。因此,如果refs/heads/master
指向某个提交,则会获得该提交,其树,其树中的任何子树以及与这些树相关联的所有blob;加上作为其父母,他们的树,子树和blob的提交,等等。对refs/heads/
中的每个名称重复一次,您就拥有基本的子图 g
。
(A fetch
使用相同的过程,除了接收者告诉发件人他有哪些SHA-1 ID作为现有分支提示。发送者可以使用它来排除从这些SHA-1可到达的对象,前提是发送者拥有那些SHA-1。这只适用于&#34;智能&#34;协议,但通常你应该使用智能协议。)
默认情况下,clone
和fetch
都会对标记采用混合方法。如果添加--tags
,除了分支名称之外,他们只使用远程提供的所有标记(顺便说一句,您可以使用git ls-remote
查看远程提供的所有引用)。如果您使用--no-tags
,则会完全忽略这些标记。否则,他们最初会忽略这些标记,像往常一样构建子图 g
,然后添加指向 g
。对于轻量级标记,这不会添加任何对象,但是对于每个带注释的标记,这会将带注释的标记对象添加到 g
(如果标记指向除提交之外的其他内容,例如另一个tag - 你也可以在这里获得更多的对象。)
一旦Git构建了克隆或获取图形 g
,它就会提取对象,从中创建一个新包,并发送该包。这意味着您不必担心现有的包含过时&#34;陈旧&#34;对象(例如,可能包含敏感数据)。
但是,如果你不想克隆,你必须考虑Git可以看到的所有引用。 git gc
和git prune
(git gc
为您运行)都将删除未引用的松散对象。显然这意味着&#34;被引用&#34;这是一个关键。但是,对于松散的物体,还有另一个豁免检定:如果他们非常年轻,Git不会删除它们。 (这可以保护在引用插入仓库之前生成的对象。)使用--prune=all
(或--expire now
)将终止这些对象。除此之外,还有一个单独的问题&#34; packed&#34;对象 - 但让我们暂时离开。
除了分支和标记名称之外,对象的其他标准引用是:
refs/remotes/
中的任何内容)refs/notes
中的任何内容)refs/stash
)HEAD
,FETCH_HEAD
,ORIG_HEAD
,MERGE_HEAD
和CHERRY_PICK_HEAD
(这些是唯一正常的但.git
中的任何文件都可以作为参考:请参阅gitrevisions
但为了安全起见,您应该查看.git/refs
和.git/packed-refs
,或者更好,更简单,使用git for-each-ref
。对于发现的每个引用,请查看它是否指向您要丢弃的提交;如果是这样,删除(或重新指向)该ref。这还包括远程跟踪分支origin/foo
(仅refs/remotes/origin/foo
)的情况,当您在foo
上删除或倒回origin
并且不在refs/remotes/origin/foo
时。我希望那些提交出现。
接下来,您需要查看reflog。如果您删除一个引用,Git(当前)也会删除它的reflog,因此这仅适用于第一次传递中未删除的引用。 reflog基本上是每个ref的平坦历史。例如,1234567...
可能在取消您正在执行/剥离的提取之前命名了提交fedcb9a...
,现在名称提交1234567...
。其reflog将包含git reflog delete ref@{number}
。如果您希望剥离该提交,则需要删除此reflog条目。您可以使用HEAD
执行此操作;见the git reflog
documentation。通常,每个分支和每个远程跟踪分支都有一个reflog,加上git unpack-objects
的一个。
看起来您已经使用git repack -a -d
处理了此问题,但我会完成此事。
即使清除了所有引用,仍然存在打包对象问题:Git可能会在包中保留未引用的对象。要摆脱它们,您必须使用.keep
强制Git创建一个新的单包文件并删除任何现有的包文件。我不确定这是否会删除包含相关git gc
文件的包文件(git gc
赢了&t 39和git repack
大多只是运行其他命令,所以它&# 39; --tags
可能会赢得其中任何一项。
1 有时是标签(如果您使用refs/heads/
)。通常,分支名称选择是&#34;所有分支名称&#34; - git clone
中的所有内容 - 但您可以指示--single-branch
仅使用{{1}}克隆一个分支。
2 这种可能性取决于对象的年龄:重复使用的最旧的对象会在包文件中结束,如果您没有将它们解包,那么它们将是安全的。 :-)编辑:另外,我最初误读了这个例子:你只是剥离提交对象,所以只要他们不在其他分支中共享你应该没问题