views:

113

answers:

2

Hi.

I cloned a project from github with git clone --mirror. That left me with a repository with a packed-refs file, a .pack and an .idx file. 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-refs but 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).

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...)

Thanks for any tips and ideas.

+8  A: 

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

The slightly longer answer is, each ref is just a 41-byte text file (40 byte SHA1 + newline) in a specific path, so the "hard" version just requires something like this in your ~/.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/Aliases is a great reference and even includes an example 'git alias' extension you can use to extend your own aliases.

clee
Thanks. I still wonder about the reasoning though...
theseion
+2  A: 

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).

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 repack actually unpacks the objects - it just reads them from the packfile, packs them with the loose ones, and makes a new pack.

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-pack source, and the pack-objects manpage says:

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.

Jefromi
That does make sense. Thanks mate.
theseion