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

Re: [PATCH 7/7] builtin/merge-tree.c: implement support for `--write-pack`

From
Taylor Blau <me@ttaylorr.com>
Date
Oct 9, 2023, 01:37 UTC
Message-ID
<ZSNZZrWyCqRH+0Bd@nand.local>
In-Reply-To
<20231008173329.GA1557002@coredump.intra.peff.net>
On Sun, Oct 08, 2023 at 01:33:29PM -0400, Jeff King wrote:
Show 68 quoted lines
> On Sun, Oct 08, 2023 at 12:04:04PM -0400, Taylor Blau wrote:
>
> > > I was interested in the same question as Junio, but from a different
> > > angle.  fast-import documentation points out that the packs it creates
> > > are suboptimal with poorer delta choices.  Are the packs created by
> > > bulk-checkin prone to the same issues?  When I was thinking in terms
> > > of having "git merge" use fast-import for pack creation instead of
> > > writing loose objects (an idea I never investigated very far), I was
> > > wondering if I'd need to mark those packs as "less optimal" and do
> > > something to make sure they were more likely to be repacked.
> > >
> > > I believe geometric repacking didn't exist back when I was thinking
> > > about this, and perhaps geometric repacking automatically handles
> > > things nicely for us.  Does it, or are we risking retaining
> > > sub-optimal deltas from the bulk-checkin code?
> > >
> > > (I've never really cracked open the pack code, so I have absolutely no
> > > idea; I'm just curious.)
> >
> > Yes, the bulk-checkin mechanism suffers from an even worse problem which
> > is the pack it creates will contain no deltas whatsoever. The contents
> > of the pack are just getting written as-is, so there's no fancy
> > delta-ficiation going on.
>
> I wonder how big a deal this would be in practice for merges.
> pack-objects will look for deltas between any two candidates objects,
> but in practice I think most deltas are between objects from multiple
> commits (across the "time" dimension, if you will) rather than within a
> single tree (the "space" dimension). And a merge operation is generally
> creating a single new tree (recursive merging may create intermediate
> states which would delta, but we don't actually need to keep those
> intermediate ones. I won't be surprised if we do, though).
>
> We should be able to test that theory by looking at existing deltas.
> Here's a script which builds an index of blobs and trees to the commits
> that introduce them:
>
>   git rev-list HEAD |
>   git diff-tree --stdin -r -m -t --raw |
>   perl -lne '
>     if (/^[0-9a-f]/) {
>       $commit = $_;
>     } elsif (/^:\S+ \S+ \S+ (\S+)/) {
>       $h{$1} = $commit;
>     }
>     END { print "$_ $h{$_}" for keys(%h) }
>   ' >commits.db
>
> And then we can see which deltas come from the same commit:
>
>   git cat-file --batch-all-objects --batch-check='%(objectname) %(deltabase)' |
>   perl -alne '
>     BEGIN {
>       open(my $fh, "<", "commits.db");
>       %commit = map { chomp; split } <$fh>;
>     }
>     next if $F[1] =~ /0{40}/; # not a delta
>     next unless defined $commit{$F[0]}; # not in index
>     print $commit{$F[0]} eq $commit{$F[1]} ? "inner" : "outer", " ", $_;
>   '
>
> In git.git, I see 460 "inner" deltas, and 193,081 "outer" ones. The
> inner ones are mostly small single-file test vectors, which makes sense.
> It's possible to have a merge result that does conflict resolution in
> two such files (that would then delta), but it seems like a fairly
> unlikely case. Numbers for linux.git are similar.
>
> So it might just not be a big issue at all for this use case.

Very interesting, thanks for running (and documenting!) this experiment. I'm mostly with you that it probably doesn't make a huge difference in practice here.

One thing that I'm not entirely clear on is how we'd treat objects that could be good delta candidates for each other between two packs. For instance, if I write a tree corresponding to the merge between two branches, it's likely that the resulting tree would be a good delta candidate against either of the trees at the tips of those two refs.

But we won't pack those trees (the ones at the tips of the refs) in the same pack as the tree containing their merge. If we later on tried to repack, would we evaluate the tip trees as possible delta candidates against the merged tree? Or would we look at the merged tree, realize it isn't delta'd with anything, and then not attempt to find any candidates?

Show 8 quoted lines
> > I think Michael Haggerty (?) suggested to me off-list that it might be
> > interesting to have a flag that we could mark packs with bad/no deltas
> > as such so that we don't implicitly trust their contents as having high
> > quality deltas.
>
> I was going to suggest the same thing. ;) Unfortunately it's a bit
> tricky to do as we have no room in the file format for an optional flag.
> You'd have to add a ".mediocre-delta" file or something.

Yeah, I figured that we'd add a new ".baddeltas" file or something. (As an aside, we probably should have an optional flags section in the .pack format, since we seem to have a lot of optional pack extensions: .rev, .bitmap, .keep, .promisor, etc.)

Show 12 quoted lines
> But here's another approach. I recall discussing a while back the idea
> that we should not necessarily trust the quality of deltas in packs that
> are pushed (and I think Thomas Gummerer even did some experiments inside
> GitHub with those, though I don't remember the results). And one way
> around that is during geometric repacking to consider the biggest/oldest
> pack as "preferred", reuse its deltas, but always compute from scratch
> with the others (neither reusing on-disk deltas, nor skipping
> try_delta() when two objects come from the same pack).
>
> That same strategy would work here (and for incremental fast-import
> packs, though of course not if your fast-import pack is the "big" one
> after you do a from-scratch import).

Yeah, definitely. I think that that code is still living in GitHub's fork, but inactive since we haven't set any of the relevant configuration in GitHub's production environment.

Show 11 quoted lines
> Possibly it exacerbates the "no deltas" issue from above (though it
> would depend on the command).  The bigger question to me is one of
> checkpointing. When do we finish off the pack with a .idx and make it
> available to other readers? We could do it at program exit, but I
> suspect there are some commands that really want to make objects
> available sooner (e.g., as soon as "git add" writes an index, we'd want
> those objects to already be available). Probably every program that
> writes objects would need to be annotated with a checkpoint call (which
> would be a noop in loose object mode).
>
> So maybe it's a dumb direction. I dunno.

I wouldn't say it's a dumb direction ;-). But I'd be hesitant pursuing it without solving the "no deltas" question from earlier.

Thanks, Taylor

Previous: Jeff KingNext: Jeff King
Message 16 of 89 in “merge-ort: implement support for packing objects together”
  1. 0/7 merge-ort: implement support for packing objects togetherTaylor Blau, Oct 6, 2023
  2. 1/7 bulk-checkin: factor out `format_object_header_hash()`Taylor Blau, Oct 6, 2023
  3. 2/7 bulk-checkin: factor out `prepare_checkpoint()`Taylor Blau, Oct 6, 2023
  4. 3/7 bulk-checkin: factor out `truncate_checkpoint()`Taylor Blau, Oct 6, 2023
  5. 4/7 bulk-checkin: factor our `finalize_checkpoint()`Taylor Blau, Oct 6, 2023
  6. 5/7 bulk-checkin: introduce `index_blob_bulk_checkin_incore()`Taylor Blau, Oct 6, 2023
  7. 6/7 bulk-checkin: introduce `index_tree_bulk_checkin_incore()`Taylor Blau, Oct 6, 2023
  8. Eric BiedermanOct 7, 2023
  9. Taylor BlauOct 9, 2023
  10. 7/7 builtin/merge-tree.c: implement support for `--write-pack`Taylor Blau, Oct 6, 2023
  11. Junio C HamanoOct 6, 2023
  12. Taylor BlauOct 6, 2023
  13. Elijah NewrenOct 8, 2023
  14. Taylor BlauOct 8, 2023
  15. Jeff KingOct 8, 2023
  16. Taylor BlauOct 9, 2023
  17. Jeff KingOct 9, 2023
  18. Junio C HamanoOct 9, 2023
  19. Patrick SteinhardtOct 9, 2023
  20. Taylor BlauOct 9, 2023
  21. Patrick SteinhardtOct 10, 2023
  22. 0/7 merge-ort: implement support for packing objects togetherTaylor Blau, Oct 17, 2023
  23. 1/7 bulk-checkin: factor out `format_object_header_hash()`Taylor Blau, Oct 17, 2023
  24. 2/7 bulk-checkin: factor out `prepare_checkpoint()`Taylor Blau, Oct 17, 2023
  25. 3/7 bulk-checkin: factor out `truncate_checkpoint()`Taylor Blau, Oct 17, 2023
  26. 4/7 bulk-checkin: factor our `finalize_checkpoint()`Taylor Blau, Oct 17, 2023
  27. 5/7 bulk-checkin: introduce `index_blob_bulk_checkin_incore()`Taylor Blau, Oct 17, 2023
  28. Junio C HamanoOct 18, 2023
  29. Taylor BlauOct 18, 2023
  30. 6/7 bulk-checkin: introduce `index_tree_bulk_checkin_incore()`Taylor Blau, Oct 17, 2023
  31. 7/7 builtin/merge-tree.c: implement support for `--write-pack`Taylor Blau, Oct 17, 2023
  32. 00/10 merge-ort: implement support for packing objects togetherTaylor Blau, Oct 18, 2023
  33. 03/10 bulk-checkin: factor out `truncate_checkpoint()`Taylor Blau, Oct 18, 2023
  34. 10/10 builtin/merge-tree.c: implement support for `--write-pack`Taylor Blau, Oct 18, 2023
  35. 07/10 bulk-checkin: generify `stream_blob_to_pack()` for arbitrary typesTaylor Blau, Oct 18, 2023
  36. 08/10 bulk-checkin: introduce `index_blob_bulk_checkin_incore()`Taylor Blau, Oct 18, 2023
  37. Junio C HamanoOct 18, 2023
  38. Taylor BlauOct 19, 2023
  39. 09/10 bulk-checkin: introduce `index_tree_bulk_checkin_incore()`Taylor Blau, Oct 18, 2023
  40. 01/10 bulk-checkin: factor out `format_object_header_hash()`Taylor Blau, Oct 18, 2023
  41. 02/10 bulk-checkin: factor out `prepare_checkpoint()`Taylor Blau, Oct 18, 2023
  42. 04/10 bulk-checkin: factor out `finalize_checkpoint()`Taylor Blau, Oct 18, 2023
  43. 05/10 bulk-checkin: extract abstract `bulk_checkin_source`Taylor Blau, Oct 18, 2023
  44. Junio C HamanoOct 18, 2023
  45. Taylor BlauOct 19, 2023
  46. Junio C HamanoOct 19, 2023
  47. 06/10 bulk-checkin: implement `SOURCE_INCORE` mode for `bulk_checkin_source`Taylor Blau, Oct 18, 2023
  48. 00/17 bloom: changed-path Bloom filters v2 (& sundries)Taylor Blau, Oct 18, 2023
  49. 01/17 t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`Taylor Blau, Oct 18, 2023
  50. 02/17 revision.c: consult Bloom filters for root commitsTaylor Blau, Oct 18, 2023
  51. 03/17 commit-graph: ensure Bloom filters are read with consistent settingsTaylor Blau, Oct 18, 2023
  52. 04/17 gitformat-commit-graph: describe version 2 of BDATTaylor Blau, Oct 18, 2023
  53. 05/17 t/helper/test-read-graph.c: extract `dump_graph_info()`Taylor Blau, Oct 18, 2023
  54. 06/17 bloom.h: make `load_bloom_filter_from_graph()` publicTaylor Blau, Oct 18, 2023
  55. 07/17 t/helper/test-read-graph: implement `bloom-filters` modeTaylor Blau, Oct 18, 2023
  56. 08/17 t4216: test changed path filters with high bit pathsTaylor Blau, Oct 18, 2023
  57. 09/17 repo-settings: introduce commitgraph.changedPathsVersionTaylor Blau, Oct 18, 2023
  58. 10/17 commit-graph: new filter ver. that fixes murmur3Taylor Blau, Oct 18, 2023
  59. 11/17 bloom: annotate filters with hash versionTaylor Blau, Oct 18, 2023
  60. 12/17 bloom: prepare to discard incompatible Bloom filtersTaylor Blau, Oct 18, 2023
  61. 13/17 commit-graph.c: unconditionally load Bloom filtersTaylor Blau, Oct 18, 2023
  62. 14/17 commit-graph: drop unnecessary `graph_read_bloom_data_context`Taylor Blau, Oct 18, 2023
  63. 15/17 object.h: fix mis-aligned flag bits tableTaylor Blau, Oct 18, 2023
  64. 16/17 commit-graph: reuse existing Bloom filters where possibleTaylor Blau, Oct 18, 2023
  65. 17/17 bloom: introduce `deinit_bloom_filters()`Taylor Blau, Oct 18, 2023
  66. Junio C HamanoOct 18, 2023
  67. Taylor BlauOct 20, 2023
  68. SZEDER GáborOct 23, 2023
  69. Taylor BlauOct 30, 2023
  70. 00/17 bloom: changed-path Bloom filters v2 (& sundries)Taylor Blau, Jan 16, 2024
  71. 01/17 t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`Taylor Blau, Jan 16, 2024
  72. 02/17 revision.c: consult Bloom filters for root commitsTaylor Blau, Jan 16, 2024
  73. 03/17 commit-graph: ensure Bloom filters are read with consistent settingsTaylor Blau, Jan 16, 2024
  74. 04/17 gitformat-commit-graph: describe version 2 of BDATTaylor Blau, Jan 16, 2024
  75. 05/17 t/helper/test-read-graph.c: extract `dump_graph_info()`Taylor Blau, Jan 16, 2024
  76. 06/17 bloom.h: make `load_bloom_filter_from_graph()` publicTaylor Blau, Jan 16, 2024
  77. 07/17 t/helper/test-read-graph: implement `bloom-filters` modeTaylor Blau, Jan 16, 2024
  78. 08/17 t4216: test changed path filters with high bit pathsTaylor Blau, Jan 16, 2024
  79. 09/17 repo-settings: introduce commitgraph.changedPathsVersionTaylor Blau, Jan 16, 2024
  80. SZEDER GáborJan 29, 2024
  81. Taylor BlauJan 29, 2024
  82. 10/17 commit-graph: new Bloom filter version that fixes murmur3Taylor Blau, Jan 16, 2024
  83. 11/17 bloom: annotate filters with hash versionTaylor Blau, Jan 16, 2024
  84. 12/17 bloom: prepare to discard incompatible Bloom filtersTaylor Blau, Jan 16, 2024
  85. 13/17 commit-graph.c: unconditionally load Bloom filtersTaylor Blau, Jan 16, 2024
  86. 14/17 commit-graph: drop unnecessary `graph_read_bloom_data_context`Taylor Blau, Jan 16, 2024
  87. 15/17 object.h: fix mis-aligned flag bits tableTaylor Blau, Jan 16, 2024
  88. 16/17 commit-graph: reuse existing Bloom filters where possibleTaylor Blau, Jan 16, 2024
  89. 17/17 bloom: introduce `deinit_bloom_filters()`Taylor Blau, Jan 16, 2024

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.