git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 03/10] builtin/pack-objects.c: learn '--assume-kept-packs-closed'

From
Jeff King <peff@peff.net>
Date
Jan 29, 2021, 22:43 UTC
Message-ID
<YBSPlO/ki5vRNX0T@coredump.intra.peff.net>
In-Reply-To
<xmqq35yjtrip.fsf@gitster.c.googlers.com>
On Fri, Jan 29, 2021 at 12:30:38PM -0800, Junio C Hamano wrote:
Show 21 quoted lines
> > I think this would generally happen if the .keep packs are generated
> > using something like "git repack -a", which packs everything reachable
> > together. So if you do:
> >
> >   git repack -ad
> >   touch .git/objects/pack/pack-whatever.keep
> >   ... some more packs come in, perhaps via pushes ...
> >   # imagine repack knew how to pass this along...
> >   git repack -a --assume-kept-packs-closed
> >
> > then you'd repack just the objects that aren't in the big pack.
> 
> Yeah.  As a tool to help the above workflow, where you are only
> creating another .keep out of youngest objects (i.e. those that are
> either loose or in non-kept packs), because by definition anything
> in .keep cannot be pointing back at these younger objects, it does
> make sense to take advantage of "the set of packs with .keep as a
> whole is closed".
> 
> It may become tricky once we start talking about creating a new
> .keep out of youngest objects PLUS a few young keep packs, though.

Right. You'd have to make sure the younger packs were also created with that reachability in mind. I.e., if you are in a situation where you've got:

  - a big "old" pack
  - N new packs from pushes

then you can't assume anything about the reachability for those individual N packs. It would be wrong to split any of them into the "keep" side. They need to have all their objects traversed until we hit something in the old pack.

But if you have a situation with:
  - a big "old" pack
  - M packs made from previous rollups on top of the old pack
  - N new packs from pushes

Then I think you can still take the old pack + the M packs as a cohesive unit closed under reachability.

The tricky part is knowing which packs are which (size is a heuristic, but it can be wrong; people may make a big push to a previously-small repository).

Show 9 quoted lines
> Starting from all on-disk .keep packs, you'd mark them as in-core
> keep bit, then drop in-core keep bit from the few young keep packs
> that you intend to coalesce with the youngest objects---that is how
> I would imagine your repacking strategy would go.  The set of all
> the on-disk .keep packs may give us "closed" guarantee, but if we 
> exclude a few latest packs from that set, would the remainder still
> give us the "closed" guarantee we can take advantage of, in order to
> pack these youngest objects (including the ones in the kept packs
> that we are coalescing)?

Yeah, I think we are going along the same lines. Except I think it is dangerous to use on-disk ".keep" as your marker, because we will racily see incoming push packs with a ".keep" (which receive-pack/index-pack use as a lockfile until the refs are updated).

So repack has to "somehow" get the list of which is which.

None of which is disputing your "it may become tricky", of course. ;) It is exactly this trickiness that I am worried about. And I am not being coy with "somehow", as if we have some custom not-yet-shared layer on top of repack that tracks this. We are still figuring out whether this is a good direction in the first place. :)

One of the things that led us to this reachability traversal, away from "just suck up all of the objects from these packs", is that this is how --unpacked works. I had always assumed it was implemented as "if it's loose, then put it in the pack". But it's not. It's attached to the revision traversal. And it actually gets some cases wrong!

It will walk every commit, so you don't have to worry about a packed commit referring to an unpacked one. But it doesn't look at the trees of the packed commits (for the quite obvious reason that doing so is orders of magnitude more expensive). That means that if there is a packed commit that refers to an unpacked blob (which is not referenced by an unpacked commit), then "rev-list --unpacked" will not report it (and likewise "git repack -d" would not pack it).

It's easy to create such a situation manually, but I've included a more plausible sequence involving "--amend" and push/fetch unpackLimit at the end of this email.

At the core, --unpacked is assuming certain things about reachability of loose/packed objects that aren't necessarily true. And this --assume-kept-pack-closed stuff is basically doing the same thing for a particular set of packs (albeit more so; I believe the patches here cut off traversal of parent pointers, not just commit-to-tree pointers).

One of the reasons I think nobody noticed with --unpacked is that the stakes are pretty low. If our assumption is wrong, the worst case is that a loose object remains unpacked in "repack -d". But we'd never delete it based on that information (instead, git prune would do its own traversal to find the correct reachability). And it would eventually get picked up by "git repack -ad".

But for repacking, the general strategy is to put things you want to keep into the new pack, and then delete the old ones (not marked as keep, of course). So if our assumption is ever wrong, it means we'd potentially drop packs that have reachable objects not found elsewhere, and we'd end up corrupting the repository.

So I think the paths forward are either:
  - come up with an air-tight system of making sure that we know packs
    we claim are closed under reachability really are (perhaps some
    marker that says "I was generated by repack -a")
  - have a "roll-up" mode that does not care about reachability at all,
    and just takes any objects from a particular set of packs (plus
    probably loose objects)

I'm still thinking aloud here, and not really sure which is a better path. I do feel like the failure modes for the second one are less risky.

Anyway, here's the --unpacked example, if you're curious. It's based on fetching, but you could invert it to do pushes (in which case it is repacking in "parent" that gets the wrong result).

-- >8 -- # two repos, one a clone of the other git init parent git -C parent commit --allow-empty -m base git clone parent child

# now there's a small fetch, which will get
# exploded into loose objects.
(
	cd parent
	echo small >small
	git add small
	git commit -m small
)
git -C child fetch

# We can verify that "rev-list --unpacked" reports these # objects. git -C child rev-list --objects --unpacked origin

# and now a bigger one that will remain a pack (we'll
# tweak unpackLimit instead of making a really big commit,
# but the concept is the same)
#
# There are two key things here:
#
#   - the "small" commit is no longer reachable, but the big one
#     still contains the "small" blob object. Using --amend is
#     a plausible mechanism for this happening.
#
#   - we are using bitmaps, which give us an exact answer for the set of
#     objects to send. Otherwise, pack-objects on the server actually
#     fails to notice the other side has told us it has the small blob, and
#     sends another copy of it.
(
	cd parent
	git repack -adb
	echo big >big
	git add big
	git commit --amend -m big
)
git -C child -c fetch.unpackLimit=1 fetch

# So now in the child we have a packed object # whose ancestor is an unpacked one. rev-list # now won't report the "small" blob (ac790413). git.compile -C child rev-list --objects --unpacked origin

# Even though we can see that it's present only as an
# unpacked object.
show_objects() {
	for i in child/.git/objects/pack/*.idx; do
		git show-index <$i
	done | cut -d' ' -f2 | sed 's/^/packed: /'
	find child/.git/objects/??/* |
		perl -F/ -alne 'print " loose: $F[-2]$F[-1]"'
}

# If we were to do an incremental repack now, it wouldn't be packed. # (Note we have to kill off the reflog, which still references the # rewound commit). rm -rf child/.git/logs git -C child repack -d show_objects

Previous: Junio C HamanoNext: Taylor Blau
Message 40 of 120 in “repack: support repacking into a geometric sequence”
  1. 00/10 repack: support repacking into a geometric sequenceTaylor Blau, Jan 19, 2021
  2. 02/10 revision: learn '--no-kept-objects'Taylor Blau, Jan 19, 2021
  3. Junio C HamanoJan 29, 2021
  4. Taylor BlauJan 29, 2021
  5. 01/10 packfile: introduce 'find_kept_pack_entry()'Taylor Blau, Jan 19, 2021
  6. Derrick StoleeJan 20, 2021
  7. Taylor BlauJan 20, 2021
  8. Junio C HamanoJan 29, 2021
  9. Taylor BlauJan 29, 2021
  10. Jeff KingJan 29, 2021
  11. Junio C HamanoJan 29, 2021
  12. 07/10 packfile: add kept-pack cache for find_kept_pack_entry()Taylor Blau, Jan 19, 2021
  13. 06/10 pack-objects: rewrite honor-pack-keep logicTaylor Blau, Jan 19, 2021
  14. 05/10 p5303: measure time to repack with keepTaylor Blau, Jan 19, 2021
  15. Junio C HamanoJan 29, 2021
  16. Jeff KingJan 29, 2021
  17. p5303: avoid sed GNU-ismJeff King, Jan 29, 2021
  18. Eric SunshineJan 29, 2021
  19. Jeff KingJan 29, 2021
  20. Eric SunshineJan 29, 2021
  21. Taylor BlauJan 29, 2021
  22. Junio C HamanoJan 29, 2021
  23. Jeff KingJan 29, 2021
  24. Junio C HamanoJan 29, 2021
  25. 08/10 builtin/pack-objects.c: teach '--keep-pack-stdin'Taylor Blau, Jan 19, 2021
  26. 10/10 builtin/repack.c: add '--geometric' optionTaylor Blau, Jan 19, 2021
  27. 03/10 builtin/pack-objects.c: learn '--assume-kept-packs-closed'Taylor Blau, Jan 19, 2021
  28. Junio C HamanoJan 29, 2021
  29. Jeff KingJan 29, 2021
  30. Taylor BlauJan 29, 2021
  31. Jeff KingJan 29, 2021
  32. Taylor BlauJan 29, 2021
  33. Jeff KingJan 29, 2021
  34. Junio C HamanoJan 29, 2021
  35. Taylor BlauJan 29, 2021
  36. Taylor BlauFeb 2, 2021
  37. Jeff KingJan 29, 2021
  38. Junio C HamanoJan 29, 2021
  39. Junio C HamanoJan 29, 2021
  40. Jeff KingJan 29, 2021
  41. Taylor BlauJan 29, 2021
  42. Jeff KingJan 29, 2021
  43. Junio C HamanoJan 29, 2021
  44. 09/10 builtin/repack.c: extract loose object handlingTaylor Blau, Jan 19, 2021
  45. Derrick StoleeJan 20, 2021
  46. Taylor BlauJan 20, 2021
  47. Derrick StoleeJan 20, 2021
  48. Junio C HamanoJan 21, 2021
  49. 04/10 p5303: add missing &&-chainsTaylor Blau, Jan 19, 2021
  50. Derrick StoleeJan 20, 2021
  51. 0/8 repack: support repacking into a geometric sequenceTaylor Blau, Feb 4, 2021
  52. 4/8 p5303: add missing &&-chainsTaylor Blau, Feb 4, 2021
  53. 3/8 builtin/pack-objects.c: add '--stdin-packs' optionTaylor Blau, Feb 4, 2021
  54. Jeff KingFeb 16, 2021
  55. Taylor BlauFeb 17, 2021
  56. Jeff KingFeb 17, 2021
  57. 2/8 revision: learn '--no-kept-objects'Taylor Blau, Feb 4, 2021
  58. Jeff KingFeb 16, 2021
  59. Taylor BlauFeb 17, 2021
  60. 1/8 packfile: introduce 'find_kept_pack_entry()'Taylor Blau, Feb 4, 2021
  61. Jeff KingFeb 16, 2021
  62. Taylor BlauFeb 16, 2021
  63. 5/8 p5303: measure time to repack with keepTaylor Blau, Feb 4, 2021
  64. Jeff KingFeb 16, 2021
  65. Jeff KingFeb 17, 2021
  66. Taylor BlauFeb 17, 2021
  67. Jeff KingFeb 17, 2021
  68. 7/8 packfile: add kept-pack cache for find_kept_pack_entry()Taylor Blau, Feb 4, 2021
  69. Jeff KingFeb 17, 2021
  70. Taylor BlauFeb 17, 2021
  71. Jeff KingFeb 17, 2021
  72. Taylor BlauFeb 17, 2021
  73. Jeff KingFeb 17, 2021
  74. 8/8 builtin/repack.c: add '--geometric' optionTaylor Blau, Feb 4, 2021
  75. Jeff KingFeb 17, 2021
  76. Taylor BlauFeb 17, 2021
  77. 6/8 builtin/pack-objects.c: rewrite honor-pack-keep logicTaylor Blau, Feb 4, 2021
  78. Jeff KingFeb 17, 2021
  79. Taylor BlauFeb 17, 2021
  80. Jeff KingFeb 17, 2021
  81. Jeff KingFeb 17, 2021
  82. Jeff KingFeb 17, 2021
  83. 0/8 repack: support repacking into a geometric sequenceTaylor Blau, Feb 18, 2021
  84. 1/8 packfile: introduce 'find_kept_pack_entry()'Taylor Blau, Feb 18, 2021
  85. 2/8 revision: learn '--no-kept-objects'Taylor Blau, Feb 18, 2021
  86. 4/8 p5303: add missing &&-chainsTaylor Blau, Feb 18, 2021
  87. 3/8 builtin/pack-objects.c: add '--stdin-packs' optionTaylor Blau, Feb 18, 2021
  88. 6/8 builtin/pack-objects.c: rewrite honor-pack-keep logicTaylor Blau, Feb 18, 2021
  89. 5/8 p5303: measure time to repack with keepTaylor Blau, Feb 18, 2021
  90. 7/8 packfile: add kept-pack cache for find_kept_pack_entry()Taylor Blau, Feb 18, 2021
  91. 8/8 builtin/repack.c: add '--geometric' optionTaylor Blau, Feb 18, 2021
  92. Jeff KingFeb 23, 2021
  93. Taylor BlauFeb 23, 2021
  94. Jeff KingFeb 23, 2021
  95. 0/8 repack: support repacking into a geometric sequenceTaylor Blau, Feb 23, 2021
  96. 1/8 packfile: introduce 'find_kept_pack_entry()'Taylor Blau, Feb 23, 2021
  97. 2/8 revision: learn '--no-kept-objects'Taylor Blau, Feb 23, 2021
  98. 3/8 builtin/pack-objects.c: add '--stdin-packs' optionTaylor Blau, Feb 23, 2021
  99. Junio C HamanoFeb 23, 2021
  100. Jeff KingFeb 23, 2021
  101. 4/8 p5303: add missing &&-chainsTaylor Blau, Feb 23, 2021
  102. 6/8 builtin/pack-objects.c: rewrite honor-pack-keep logicTaylor Blau, Feb 23, 2021
  103. 5/8 p5303: measure time to repack with keepTaylor Blau, Feb 23, 2021
  104. 8/8 builtin/repack.c: add '--geometric' optionTaylor Blau, Feb 23, 2021
  105. Junio C HamanoFeb 24, 2021
  106. Junio C HamanoFeb 24, 2021
  107. Taylor BlauMar 4, 2021
  108. Taylor BlauMar 4, 2021
  109. 7/8 packfile: add kept-pack cache for find_kept_pack_entry()Taylor Blau, Feb 23, 2021
  110. Jeff KingFeb 23, 2021
  111. Junio C HamanoFeb 23, 2021
  112. Jeff KingFeb 23, 2021
  113. Martin FickFeb 23, 2021
  114. Taylor BlauFeb 23, 2021
  115. Martin FickFeb 23, 2021
  116. Jeff KingFeb 23, 2021
  117. Martin FickFeb 23, 2021
  118. Jeff KingFeb 23, 2021
  119. Martin FickFeb 24, 2021
  120. Jeff KingFeb 26, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.