git 如何解压packed-refs?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3333255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 04:26:55  来源:igfitidea点击:

How to unpack packed-refs?

gitcommand-line

提问by Max Leske

I cloned a project from github with git clone --mirror. That left me with a repository with a packed-refsfile, a .packand an .idxfile. For developement purposes I want to look at the loose objects, so I unpacked the objects with git unpack-objects < <pack file>which worked fine (I unpacked the pack file into a new repo if you're wondering). Only thing is that refs/heads/is still empty, all the refs are still only in packed-refsbut I need them in refs/heads/. I wasn't able to find a command that would extract or unpack those references and I can somehow not believe that I would have to do this by hand (or via pipes).

我用git clone --mirror从 github 克隆了一个项目。这给我留下了一个存储库,其中包含一个打包的 refs文件、一个.pack和一个.idx文件。出于开发目的,我想查看松散的对象,因此我使用git unpack-objects < <pack file> 解压缩了对象,效果很好(如果您想知道,我将打包文件解压缩到新的存储库中)。唯一的问题是refs/heads/仍然是空的,所有的 refs 仍然只在打包的 refs 中,但我需要它们在refs/heads/ 中。我无法找到可以提取或解压缩这些引用的命令,而且我无法相信我必须手动(或通过管道)执行此操作。

So actually I have two questions:

所以实际上我有两个问题:

  1. Is there an easy way to "restore" refs from packed-refs?
  2. If not, why isn't there? If there's a command for unpacking objects, what is the reasoning behind not providing the same for refs (don't forget that there's even a command git pack-refs...)
  1. 有没有一种简单的方法可以从打包引用中“恢复”引用
  2. 如果没有,为什么没有?如果有一个用于解包对象的命令,那么不为 refs 提供相同内容的原因是什么(不要忘记甚至还有一个命令git pack-refs...)

Thanks for any tips and ideas.

感谢您提供任何提示和想法。

回答by clee

The short answer is "no" - there is no "easy way" to unpack the refs the way you're asking.

简短的回答是“不” - 没有“简单的方法”可以按照您要求的方式解压 refs。

The slightly longer answer is, each ref is just a 41-byte text file (40 byte SHA1 in hex + newline) in a specific path, so the "hard" version just requires something like this in your ~/.gitconfig:

稍微长一点的答案是,每个 ref 只是特定路径中的 41 字节文本文件(十六进制 + 换行符中的 40 字节 SHA1),因此“硬”版本只需要在您的~/.gitconfig.

[alias]
unpack-refs = "!bash -c 'IFS=$''\n''; for f in $(git show-ref --heads); do /bin/echo ''Writing  '' $(echo $f | cut -c42-); echo $(echo $f | cut -c1-40) > \"${GIT_DIR:-.git}/$(echo $f | cut -c42-)\"; done'"

Took a little trickiness to figure out how to get it to work properly, but there you go! Now you have 'git unpack-refs' and it does what you expect, and as a bonus it even works with $GIT_DIR if that's set (otherwise it assumes you're in the root of the git tree). If you haven't read up on git aliases, https://git.wiki.kernel.org/index.php/Aliasesis a great reference and even includes an example 'git alias' extension you can use to extend your own aliases.

弄清楚如何让它正常工作需要一点技巧,但是你去了!现在你有了 'git unpack-refs' 并且它会做你所期望的,作为奖励,如果设置了它,它甚至可以与 $GIT_DIR 一起使用(否则它假设你在 git 树的根目录中)。如果您还没有阅读过 git 别名,https://git.wiki.kernel.org/index.php/Aliases是一个很好的参考,甚至包括一个示例“git 别名”扩展,您可以使用它来扩展您自己的别名.

回答by Cascabel

The reason the packed refs exist is to speed up access in a repo with zillions of refs - it's easier to look at a single file with many lines than to hit the file system once for every single ref. Anything in git which needs to know about refs goes through code which can read both the refs directory and the packed refs file. Unpacking it would defeat its purpose. If you want to access refs, use the plumbing commands (e.g. show-ref, for-each-ref, update-ref...). I can't really think of any kind of access which would be faster and easier with the directory structure than with the plumbing commands (especially with for-each-ref available).

打包 ref 存在的原因是为了加快在具有无数 ref 的 repo 中的访问速度 - 查看具有多行的单个文件比为每个单个 ref 访问一次文件系统更容易。git 中需要了解 refs 的任何内容都通过可以读取 refs 目录和打包的 refs 文件的代码。拆开它的包装会破坏它的目的。如果您想访问 refs,请使用管道命令(例如 show-ref、for-each-ref、update-ref...)。我真的想不出任何类型的访问会比使用管道命令更快、更容易使用目录结构(尤其是使用 for-each-ref 可用)。

And yes, packed objects are (like packed refs) created for improved performance, but there's a huge difference. A packed refs file is just a bunch of independent lines. You can, essentially for free, add to or remove from it. There's no need to unpack it in order to modify it. Packed objects, on the other hand, are delta-compressed, so the objects inside depend on each other. They greatly reduce disk usage, and objects can be read from them at reasonable cost, but attempting to modify the set of objects in the pack is much more expensive than modifying loose objects, so it's only done periodically by git repack(called by git gc), though I don't believe git repackactually unpacks the objects - it just reads them from the packfile, packs them with the loose ones, and makes a new pack.

是的,打包对象(如打包引用)是为了提高性能而创建的,但存在巨大差异。打包的 refs 文件只是一堆独立的行。您基本上可以免费添加或删除它。无需解压即可修改。另一方面,打包的对象是增量压缩的,因此内部的对象相互依赖。它们大大减少了磁盘使用量,并且可以以合理的成本从中读取对象,但是尝试修改包中的对象集比修改松散对象要昂贵得多,因此它只能由git repack(由 调用git gc)定期完成,尽管我不要相信git repack实际上会解包对象 - 它只是从包文件中读取它们,将它们与松散的打包在一起,然后制作一个新包。

However, when a pack is transferred from a remote, it's unpacked on the local side. I see a call to an unpack method in the git receive-packsource, and the pack-objectsmanpage says:

但是,当从远程传输包时,它会在本地解包。我在git receive-pack源代码中看到对 unpack 方法的调用,并且pack-objects联机帮助页说:

The git unpack-objects command can read the packed archive and expand the objects contained in the pack into "one-file one-object" format; this is typically done by the smart-pull commands when a pack is created on-the-fly for efficient network transport by their peers.

git unpack-objects 命令可以读取打包后的压缩包,将压缩包中包含的对象展开为“一文件一对象”格式;这通常由 smart-pull 命令在动态创建包以供其对等方进行高效网络传输时完成。

回答by Schlacki

Yet another answer to your question 1:

你的问题1的另一个答案:

I assume you already used a loop like this to use git unpack-objects:

我假设您已经使用过这样的循环来使用 git unpack-objects:

 mkdir CLONE
 mv .git/objects/pack/* CLONE/
 for pack in CLONE/*.pack; do
    git unpack-objects < $pack
 done
 rm -rf CLONE/
 mkdir CLONE
 mv .git/objects/pack/* CLONE/
 for pack in CLONE/*.pack; do
    git unpack-objects < $pack
 done
 rm -rf CLONE/

which unpacks all objects, but leaves the packed branch heads in the file .git/packed-refs

它解包所有对象,但将打包的分支头留在文件 .git/packed-refs 中

Thanks to Clee's answer, which I reused here, I found that the following commands will be needed to unpack the branch heads, and clean up:

感谢我在这里重用的 Clee 的回答,我发现需要以下命令来解压分支头并进行清理:

(
   IFS=$'\n'; # set the input field separator to new line
   for f in $(git show-ref --heads); do
      ref_hash="$(echo $f | cut -c1-40)"
      ref_label="$(echo $f | cut -c42-)"
      echo " unpack: $ref_hash $ref_label"
      echo "$ref_hash" > ".git/$ref_label";
      sed -i "s~^${ref_hash} ${ref_label}$~~" .git/packed-refs
      sed -i '/^$/d'                           .git/packed-refs
   done
   rm .git/info/refs
   rm .git/objects/info/packs
)
(
   IFS=$'\n'; # set the input field separator to new line
   for f in $(git show-ref --heads); do
      ref_hash="$(echo $f | cut -c1-40)"
      ref_label="$(echo $f | cut -c42-)"
      echo " unpack: $ref_hash $ref_label"
      echo "$ref_hash" > ".git/$ref_label";
      sed -i "s~^${ref_hash} ${ref_label}$~~" .git/packed-refs
      sed -i '/^$/d'                           .git/packed-refs
   done
   rm .git/info/refs
   rm .git/objects/info/packs
)

Note the difference to Clee's answer:

请注意与 Clee 的回答的不同之处:

A) the packed refs are removed from the .git/packed-refs file, and

A) 从 .git/packed-refs 文件中删除了打包的 refs,并且

B) the files .git/info/refs and .git/objects/info/packs are deleted.

B) 文件 .git/info/refs 和 .git/objects/info/packs 被删除。

I admit that it might not be a good idea to delete files just like that in the .git folder, however this is what I needed to do in order to do a clean unpack.

我承认删除 .git 文件夹中的文件可能不是一个好主意,但是这是我需要做的才能进行干净的解包。

Question 2 is still not answered though.

问题 2 仍然没有回答。

回答by codeplay

There is a workaround that worked for me and might work for some of you:

有一种解决方法对我有用,并且可能对你们中的一些人有用:

Basically, Delete all the tags locally (removes it from packed-refs) and then fetch it again.

基本上,在本地删除所有标签(从打包引用中删除它),然后再次获取它。

git tag | xargs git tag -d && git fetch --tags