Skip to content

[DO NOT SUBMIT] Multi-pack-index cleanups #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ceab693
multi-pack-index: add design document
derrickstolee Jul 12, 2018
e0d1bcf
multi-pack-index: add format details
derrickstolee Jul 12, 2018
6a257f0
multi-pack-index: add builtin
derrickstolee Jul 12, 2018
a340773
multi-pack-index: add 'write' verb
derrickstolee Jul 12, 2018
fc59e74
midx: write header information to lockfile
derrickstolee Jul 12, 2018
4d80560
multi-pack-index: load into memory
derrickstolee Jul 12, 2018
2c38133
t5319: expand test data
derrickstolee Jul 12, 2018
9208e31
packfile: generalize pack directory list
derrickstolee Jul 12, 2018
396f257
multi-pack-index: read packfile list
derrickstolee Jul 12, 2018
32f3c54
multi-pack-index: write pack names in chunk
derrickstolee Jul 12, 2018
3227565
midx: read pack names into array
derrickstolee Jul 12, 2018
fe1ed56
midx: sort and deduplicate objects from packfiles
derrickstolee Jul 12, 2018
0d5b3a5
midx: write object ids in a chunk
derrickstolee Jul 12, 2018
d7cacf2
midx: write object id fanout chunk
derrickstolee Jul 12, 2018
662148c
midx: write object offsets
derrickstolee Jul 12, 2018
c4d2522
config: create core.multiPackIndex setting
derrickstolee Jul 12, 2018
3715a63
midx: read objects from multi-pack-index
derrickstolee Jul 12, 2018
8aac67a
midx: use midx in abbreviation calculations
derrickstolee Jul 12, 2018
a40498a
midx: use existing midx when writing new one
derrickstolee Jul 12, 2018
b8990fb
midx: use midx in approximate_object_count
derrickstolee Jul 12, 2018
f3a002b
midx: prevent duplicate packfile loads
derrickstolee Jul 12, 2018
17c35c8
packfile: skip loading index if in multi-pack-index
derrickstolee Jul 12, 2018
525e18c
midx: clear midx on repack
derrickstolee Jul 12, 2018
e1f1c62
Merge remote-tracking branch 'junio/ds/multi-pack-index' into jk/for-…
derrickstolee Aug 15, 2018
444e0dc
multi-pack-index: provide more helpful usage info
derrickstolee Aug 15, 2018
db8a956
multi-pack-index: store local property
derrickstolee Aug 15, 2018
75ce0c2
midx: mark bad packed objects
derrickstolee Aug 17, 2018
9855c10
midx: stop reporting garbage
derrickstolee Aug 17, 2018
6a4a56a
midx: fix bug that skips midx with alternates
derrickstolee Aug 17, 2018
ac27d0b
packfile: add all_packs list
derrickstolee Aug 17, 2018
8fe8697
treewide: use get_all_packs
derrickstolee Aug 17, 2018
f3bcdc6
midx: test a few commands that use get_all_packs
derrickstolee Aug 17, 2018
4eb5df3
pack-objects: consider packs in multi-pack-index
derrickstolee Aug 20, 2018
098dd1d
DO-NOT-MERGE: compute multi-pack-index on repack
derrickstolee Aug 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@
/git-mergetool--lib
/git-mktag
/git-mktree
/git-name-rev
/git-multi-pack-index
/git-mv
/git-name-rev
/git-notes
/git-p4
/git-pack-redundant
Expand Down
5 changes: 5 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,11 @@ gc.commitGraph::
required. Default is false. See linkgit:git-commit-graph[1]
for details.

core.multiPackIndex::
Use the multi-pack-index file to track multiple packfiles using a
single index. See link:technical/multi-pack-index.html[the
multi-pack-index design document].

core.sparseCheckout::
Enable "sparse checkout" feature. See section "Sparse checkout" in
linkgit:git-read-tree[1] for more information.
Expand Down
56 changes: 56 additions & 0 deletions Documentation/git-multi-pack-index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
git-multi-pack-index(1)
=======================

NAME
----
git-multi-pack-index - Write and verify multi-pack-indexes


SYNOPSIS
--------
[verse]
'git multi-pack-index' [--object-dir=<dir>] <verb>

DESCRIPTION
-----------
Write or verify a multi-pack-index (MIDX) file.

OPTIONS
-------

--object-dir=<dir>::
Use given directory for the location of Git objects. We check
`<dir>/packs/multi-pack-index` for the current MIDX file, and
`<dir>/packs` for the pack-files to index.

write::
When given as the verb, write a new MIDX file to
`<dir>/packs/multi-pack-index`.


EXAMPLES
--------

* Write a MIDX file for the packfiles in the current .git folder.
+
-----------------------------------------------
$ git multi-pack-index write
-----------------------------------------------

* Write a MIDX file for the packfiles in an alternate object store.
+
-----------------------------------------------
$ git multi-pack-index --object-dir <alt> write
-----------------------------------------------


SEE ALSO
--------
See link:technical/multi-pack-index.html[The Multi-Pack-Index Design
Document] and link:technical/pack-format.html[The Multi-Pack-Index
Format] for more information on the multi-pack-index feature.


GIT
---
Part of the linkgit:git[1] suite
109 changes: 109 additions & 0 deletions Documentation/technical/multi-pack-index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
Multi-Pack-Index (MIDX) Design Notes
====================================

The Git object directory contains a 'pack' directory containing
packfiles (with suffix ".pack") and pack-indexes (with suffix
".idx"). The pack-indexes provide a way to lookup objects and
navigate to their offset within the pack, but these must come
in pairs with the packfiles. This pairing depends on the file
names, as the pack-index differs only in suffix with its pack-
file. While the pack-indexes provide fast lookup per packfile,
this performance degrades as the number of packfiles increases,
because abbreviations need to inspect every packfile and we are
more likely to have a miss on our most-recently-used packfile.
For some large repositories, repacking into a single packfile
is not feasible due to storage space or excessive repack times.

The multi-pack-index (MIDX for short) stores a list of objects
and their offsets into multiple packfiles. It contains:

- A list of packfile names.
- A sorted list of object IDs.
- A list of metadata for the ith object ID including:
- A value j referring to the jth packfile.
- An offset within the jth packfile for the object.
- If large offsets are required, we use another list of large
offsets similar to version 2 pack-indexes.

Thus, we can provide O(log N) lookup time for any number
of packfiles.

Design Details
--------------

- The MIDX is stored in a file named 'multi-pack-index' in the
.git/objects/pack directory. This could be stored in the pack
directory of an alternate. It refers only to packfiles in that
same directory.

- The pack.multiIndex config setting must be on to consume MIDX files.

- The file format includes parameters for the object ID hash
function, so a future change of hash algorithm does not require
a change in format.

- The MIDX keeps only one record per object ID. If an object appears
in multiple packfiles, then the MIDX selects the copy in the most-
recently modified packfile.

- If there exist packfiles in the pack directory not registered in
the MIDX, then those packfiles are loaded into the `packed_git`
list and `packed_git_mru` cache.

- The pack-indexes (.idx files) remain in the pack directory so we
can delete the MIDX file, set core.midx to false, or downgrade
without any loss of information.

- The MIDX file format uses a chunk-based approach (similar to the
commit-graph file) that allows optional data to be added.

Future Work
-----------

- Add a 'verify' subcommand to the 'git midx' builtin to verify the
contents of the multi-pack-index file match the offsets listed in
the corresponding pack-indexes.

- The multi-pack-index allows many packfiles, especially in a context
where repacking is expensive (such as a very large repo), or
unexpected maintenance time is unacceptable (such as a high-demand
build machine). However, the multi-pack-index needs to be rewritten
in full every time. We can extend the format to be incremental, so
writes are fast. By storing a small "tip" multi-pack-index that
points to large "base" MIDX files, we can keep writes fast while
still reducing the number of binary searches required for object
lookups.

- The reachability bitmap is currently paired directly with a single
packfile, using the pack-order as the object order to hopefully
compress the bitmaps well using run-length encoding. This could be
extended to pair a reachability bitmap with a multi-pack-index. If
the multi-pack-index is extended to store a "stable object order"
(a function Order(hash) = integer that is constant for a given hash,
even as the multi-pack-index is updated) then a reachability bitmap
could point to a multi-pack-index and be updated independently.

- Packfiles can be marked as "special" using empty files that share
the initial name but replace ".pack" with ".keep" or ".promisor".
We can add an optional chunk of data to the multi-pack-index that
records flags of information about the packfiles. This allows new
states, such as 'repacked' or 'redeltified', that can help with
pack maintenance in a multi-pack environment. It may also be
helpful to organize packfiles by object type (commit, tree, blob,
etc.) and use this metadata to help that maintenance.

- The partial clone feature records special "promisor" packs that
may point to objects that are not stored locally, but available
on request to a server. The multi-pack-index does not currently
track these promisor packs.

Related Links
-------------
[0] https://bugs.chromium.org/p/git/issues/detail?id=6
Chromium work item for: Multi-Pack Index (MIDX)

[1] https://public-inbox.org/git/20180107181459.222909-1-dstolee@microsoft.com/
An earlier RFC for the multi-pack-index feature

[2] https://public-inbox.org/git/alpine.DEB.2.20.1803091557510.23109@alexmv-linux/
Git Merge 2018 Contributor's summit notes (includes discussion of MIDX)
77 changes: 77 additions & 0 deletions Documentation/technical/pack-format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,80 @@ Pack file entry: <+
corresponding packfile.

20-byte SHA-1-checksum of all of the above.

== multi-pack-index (MIDX) files have the following format:

The multi-pack-index files refer to multiple pack-files and loose objects.

In order to allow extensions that add extra data to the MIDX, we organize
the body into "chunks" and provide a lookup table at the beginning of the
body. The header includes certain length values, such as the number of packs,
the number of base MIDX files, hash lengths and types.

All 4-byte numbers are in network order.

HEADER:

4-byte signature:
The signature is: {'M', 'I', 'D', 'X'}

1-byte version number:
Git only writes or recognizes version 1.

1-byte Object Id Version
Git only writes or recognizes version 1 (SHA1).

1-byte number of "chunks"

1-byte number of base multi-pack-index files:
This value is currently always zero.

4-byte number of pack files

CHUNK LOOKUP:

(C + 1) * 12 bytes providing the chunk offsets:
First 4 bytes describe chunk id. Value 0 is a terminating label.
Other 8 bytes provide offset in current file for chunk to start.
(Chunks are provided in file-order, so you can infer the length
using the next chunk position if necessary.)

The remaining data in the body is described one chunk at a time, and
these chunks may be given in any order. Chunks are required unless
otherwise specified.

CHUNK DATA:

Packfile Names (ID: {'P', 'N', 'A', 'M'})
Stores the packfile names as concatenated, null-terminated strings.
Packfiles must be listed in lexicographic order for fast lookups by
name. This is the only chunk not guaranteed to be a multiple of four
bytes in length, so should be the last chunk for alignment reasons.

OID Fanout (ID: {'O', 'I', 'D', 'F'})
The ith entry, F[i], stores the number of OIDs with first
byte at most i. Thus F[255] stores the total
number of objects.

OID Lookup (ID: {'O', 'I', 'D', 'L'})
The OIDs for all objects in the MIDX are stored in lexicographic
order in this chunk.

Object Offsets (ID: {'O', 'O', 'F', 'F'})
Stores two 4-byte values for every object.
1: The pack-int-id for the pack storing this object.
2: The offset within the pack.
If all offsets are less than 2^31, then the large offset chunk
will not exist and offsets are stored as in IDX v1.
If there is at least one offset value larger than 2^32-1, then
the large offset chunk must exist. If the large offset chunk
exists and the 31st bit is on, then removing that bit reveals
the row in the large offsets containing the 8-byte offset of
this object.

[Optional] Object Large Offsets (ID: {'L', 'O', 'F', 'F'})
8-byte offsets into large packfiles.

TRAILER:

20-byte SHA1-checksum of the above contents.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ TEST_BUILTINS_OBJS += test-online-cpus.o
TEST_BUILTINS_OBJS += test-path-utils.o
TEST_BUILTINS_OBJS += test-prio-queue.o
TEST_BUILTINS_OBJS += test-read-cache.o
TEST_BUILTINS_OBJS += test-read-midx.o
TEST_BUILTINS_OBJS += test-ref-store.o
TEST_BUILTINS_OBJS += test-regex.o
TEST_BUILTINS_OBJS += test-repository.o
Expand Down Expand Up @@ -892,6 +893,7 @@ LIB_OBJS += merge.o
LIB_OBJS += merge-blobs.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += mergesort.o
LIB_OBJS += midx.o
LIB_OBJS += name-hash.o
LIB_OBJS += negotiator/default.o
LIB_OBJS += negotiator/skipping.o
Expand Down Expand Up @@ -1051,6 +1053,7 @@ BUILTIN_OBJS += builtin/merge-recursive.o
BUILTIN_OBJS += builtin/merge-tree.o
BUILTIN_OBJS += builtin/mktag.o
BUILTIN_OBJS += builtin/mktree.o
BUILTIN_OBJS += builtin/multi-pack-index.o
BUILTIN_OBJS += builtin/mv.o
BUILTIN_OBJS += builtin/name-rev.o
BUILTIN_OBJS += builtin/notes.o
Expand Down
1 change: 1 addition & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
extern int cmd_mktag(int argc, const char **argv, const char *prefix);
extern int cmd_mktree(int argc, const char **argv, const char *prefix);
extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
extern int cmd_mv(int argc, const char **argv, const char *prefix);
extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
extern int cmd_notes(int argc, const char **argv, const char *prefix);
Expand Down
2 changes: 1 addition & 1 deletion builtin/count-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
struct strbuf pack_buf = STRBUF_INIT;
struct strbuf garbage_buf = STRBUF_INIT;

for (p = get_packed_git(the_repository); p; p = p->next) {
for (p = get_all_packs(the_repository); p; p = p->next) {
if (!p->pack_local)
continue;
if (open_pack_index(p))
Expand Down
4 changes: 2 additions & 2 deletions builtin/fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
struct progress *progress = NULL;

if (show_progress) {
for (p = get_packed_git(the_repository); p;
for (p = get_all_packs(the_repository); p;
p = p->next) {
if (open_pack_index(p))
continue;
Expand All @@ -749,7 +749,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)

progress = start_progress(_("Checking objects"), total);
}
for (p = get_packed_git(the_repository); p;
for (p = get_all_packs(the_repository); p;
p = p->next) {
/* verify gives error messages itself */
if (verify_pack(p, fsck_obj_buffer,
Expand Down
4 changes: 2 additions & 2 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ static struct packed_git *find_base_packs(struct string_list *packs,
{
struct packed_git *p, *base = NULL;

for (p = get_packed_git(the_repository); p; p = p->next) {
for (p = get_all_packs(the_repository); p; p = p->next) {
if (!p->pack_local)
continue;
if (limit) {
Expand All @@ -208,7 +208,7 @@ static int too_many_packs(void)
if (gc_auto_pack_limit <= 0)
return 0;

for (cnt = 0, p = get_packed_git(the_repository); p; p = p->next) {
for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) {
if (!p->pack_local)
continue;
if (p->pack_keep)
Expand Down
47 changes: 47 additions & 0 deletions builtin/multi-pack-index.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "builtin.h"
#include "cache.h"
#include "config.h"
#include "parse-options.h"
#include "midx.h"

static char const * const builtin_multi_pack_index_usage[] = {
N_("git multi-pack-index [--object-dir=<dir>] write"),
NULL
};

static struct opts_multi_pack_index {
const char *object_dir;
} opts;

int cmd_multi_pack_index(int argc, const char **argv,
const char *prefix)
{
static struct option builtin_multi_pack_index_options[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
OPT_END(),
};

git_config(git_default_config, NULL);

argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage, 0);

if (!opts.object_dir)
opts.object_dir = get_object_directory();

if (argc == 0)
usage_with_options(builtin_multi_pack_index_usage,
builtin_multi_pack_index_options);

if (argc > 1) {
die(_("too many arguments"));
return 1;
}

if (!strcmp(argv[0], "write"))
return write_midx_file(opts.object_dir);

die(_("unrecognized verb: %s"), argv[0]);
}
Loading