Subject: Re: recognize loose local objects during repack

From: Junio C Hamano <[email-masked]>
Date: Mon, 10 Nov 2008 13:03:10 -0800

[email-masked] writes:

> This was developed on top of the previous repack/pack-objects series.

Thanks.  Looked alright from a cursory reading.

By the way, I've been meaning to suggest that we should straighten out the
semantics of "unpacked" vs "incremental".

What the latter means is quite clear.  We are creating a new packfile to
bundle loose ones into one, and after the new packfile is installed we can
remove the loose objects.

But what --unpacked means often confuses people, primarily because it is a
performance heuristics that makes certain assumptions on how the objects
are packed.

Namely, "unpacked" is about discovery process of objects to be packed.
Without the option, we enumerate all objects that are reachable from the
commits in the given range, excluding the trees and blobs that should
exist in commits that are excluded (e.g, if you say "--objects A..B", we
exclude trees and blobs referenced by commit A).

With the option, we also omit commits that are packed.  What's funny is
that their trees and blobs are omitted, even if they are loose ;-)

This is typically not an issue, because you do not say "pack only this
commit object, without its trees or blobs" when repacking, and because you
must have all the trees and blobs necessary for a commit available when
you pack a commit; for these reasons, the trees and blobs are typically
packed together with the commit.  It is not an issue that the rev-list
with --unpacked option does not list loose trees and blobs that belong to
a packed commit for this reason.

You could however arrange so that a commit itself is packed but some of
the tree and blob objects it refers to are loose, in which case these
loose objects may not ever get repacked incrementally.

In an empty directory, try this:

        git init &&
        echo 0 >file && git add file && git commit -m initial &&

        P=$(git rev-list HEAD | git pack-objects pack) &&
        mv pack-$P.* .git/objects/pack/ &&
        git prune && git count-objects -v

        git repack && git count-objects -v

It packs only the commit object (and prunes it), leaving a tree and a blob
loose.  The repack won't find anything to pack.

This is not so bad in the sense that it will never corrupt your
repository, but it is confusing.  Admittedly, not peeking into a commit
that is packed is a reasonable good heuristics for performance reasons.

Interestingly enough, the object listing machinery do traverse into
parents of packed commits when --unpacked is given.  So if you pack a
commit and arrange to keep its parents unpacked, they are subject to the
incremental repacking.  In other words, the performance heuristics may not
be buying us very much --- we are traversing the history down to the root
commits regardless.