builtin/index-pack.c: write reverse indexes

Teach 'git index-pack' to optionally write and verify reverse index with '--[no-]rev-index', as well as respecting the 'pack.writeReverseIndex' configuration option. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Jan 25, 2021 at 18:37 UTC e37d0b8730b67b83c3a7cc20ed3a45cf7f0aa7ed
3 files changed +131 -8
Documentation/git-index-pack.txt
+13 -5
@@ -9,17 +9,18 @@ git-index-pack - Build pack index file for an existing packed archive
9 SYNOPSIS
10 --------
11 [verse]
12 -'git index-pack' [-v] [-o <index-file>] <pack-file>
12 +'git index-pack' [-v] [-o <index-file>] [--[no-]rev-index] <pack-file>
13 'git index-pack' --stdin [--fix-thin] [--keep] [-v] [-o <index-file>]
14 - [<pack-file>]
14 + [--[no-]rev-index] [<pack-file>]
15
16
17 DESCRIPTION
18 -----------
19 Reads a packed archive (.pack) from the specified file, and
20 -builds a pack index file (.idx) for it. The packed archive
21 -together with the pack index can then be placed in the
22 -objects/pack/ directory of a Git repository.
20 +builds a pack index file (.idx) for it. Optionally writes a
21 +reverse-index (.rev) for the specified pack. The packed
22 +archive together with the pack index can then be placed in
23 +the objects/pack/ directory of a Git repository.
24
25
26 OPTIONS
@@ -35,6 +36,13 @@ OPTIONS
36 fails if the name of packed archive does not end
37 with .pack).
38
39 +--[no-]rev-index::
40 + When this flag is provided, generate a reverse index
41 + (a `.rev` file) corresponding to the given pack. If
42 + `--verify` is given, ensure that the existing
43 + reverse index is correct. Takes precedence over
44 + `pack.writeReverseIndex`.
45 +
46 --stdin::
47 When this flag is provided, the pack is read from stdin
48 instead and a copy is then written to <pack-file>. If
builtin/index-pack.c
+47 -3
@@ -17,7 +17,7 @@
17 #include "promisor-remote.h"
18
19 static const char index_pack_usage[] =
20 -"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
20 +"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
21
22 struct object_entry {
23 struct pack_idx_entry idx;
@@ -1484,12 +1484,14 @@ static void write_special_file(const char *suffix, const char *msg,
1484
1485 static void final(const char *final_pack_name, const char *curr_pack_name,
1486 const char *final_index_name, const char *curr_index_name,
1487 + const char *final_rev_index_name, const char *curr_rev_index_name,
1488 const char *keep_msg, const char *promisor_msg,
1489 unsigned char *hash)
1490 {
1491 const char *report = "pack";
1492 struct strbuf pack_name = STRBUF_INIT;
1493 struct strbuf index_name = STRBUF_INIT;
1494 + struct strbuf rev_index_name = STRBUF_INIT;
1495 int err;
1496
1497 if (!from_stdin) {
@@ -1524,6 +1526,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
1526 } else
1527 chmod(final_index_name, 0444);
1528
1529 + if (curr_rev_index_name) {
1530 + if (final_rev_index_name != curr_rev_index_name) {
1531 + if (!final_rev_index_name)
1532 + final_rev_index_name = odb_pack_name(&rev_index_name, hash, "rev");
1533 + if (finalize_object_file(curr_rev_index_name, final_rev_index_name))
1534 + die(_("cannot store reverse index file"));
1535 + } else
1536 + chmod(final_rev_index_name, 0444);
1537 + }
1538 +
1539 if (do_fsck_object) {
1540 struct packed_git *p;
1541 p = add_packed_git(final_index_name, strlen(final_index_name), 0);
@@ -1553,6 +1565,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
1565 }
1566 }
1567
1568 + strbuf_release(&rev_index_name);
1569 strbuf_release(&index_name);
1570 strbuf_release(&pack_name);
1571 }
@@ -1578,6 +1591,12 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
1591 }
1592 return 0;
1593 }
1594 + if (!strcmp(k, "pack.writereverseindex")) {
1595 + if (git_config_bool(k, v))
1596 + opts->flags |= WRITE_REV;
1597 + else
1598 + opts->flags &= ~WRITE_REV;
1599 + }
1600 return git_default_config(k, v, cb);
1601 }
1602
@@ -1695,12 +1714,14 @@ static void show_pack_info(int stat_only)
1714
1715 int cmd_index_pack(int argc, const char **argv, const char *prefix)
1716 {
1698 - int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
1717 + int i, fix_thin_pack = 0, verify = 0, stat_only = 0, rev_index;
1718 const char *curr_index;
1700 - const char *index_name = NULL, *pack_name = NULL;
1719 + const char *curr_rev_index = NULL;
1720 + const char *index_name = NULL, *pack_name = NULL, *rev_index_name = NULL;
1721 const char *keep_msg = NULL;
1722 const char *promisor_msg = NULL;
1723 struct strbuf index_name_buf = STRBUF_INIT;
1724 + struct strbuf rev_index_name_buf = STRBUF_INIT;
1725 struct pack_idx_entry **idx_objects;
1726 struct pack_idx_option opts;
1727 unsigned char pack_hash[GIT_MAX_RAWSZ];
@@ -1727,6 +1748,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
1748 if (prefix && chdir(prefix))
1749 die(_("Cannot come back to cwd"));
1750
1751 + rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
1752 +
1753 for (i = 1; i < argc; i++) {
1754 const char *arg = argv[i];
1755
@@ -1805,6 +1828,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
1828 if (hash_algo == GIT_HASH_UNKNOWN)
1829 die(_("unknown hash algorithm '%s'"), arg);
1830 repo_set_hash_algo(the_repository, hash_algo);
1831 + } else if (!strcmp(arg, "--rev-index")) {
1832 + rev_index = 1;
1833 + } else if (!strcmp(arg, "--no-rev-index")) {
1834 + rev_index = 0;
1835 } else
1836 usage(index_pack_usage);
1837 continue;
@@ -1826,6 +1853,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
1853 if (!index_name && pack_name)
1854 index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
1855
1856 + opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
1857 + if (rev_index) {
1858 + opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
1859 + if (index_name)
1860 + rev_index_name = derive_filename(index_name,
1861 + "idx", "rev",
1862 + &rev_index_name_buf);
1863 + }
1864 +
1865 if (verify) {
1866 if (!index_name)
1867 die(_("--verify with no packfile name given"));
@@ -1878,11 +1914,16 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
1914 for (i = 0; i < nr_objects; i++)
1915 idx_objects[i] = &objects[i].idx;
1916 curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
1917 + if (rev_index)
1918 + curr_rev_index = write_rev_file(rev_index_name, idx_objects,
1919 + nr_objects, pack_hash,
1920 + opts.flags);
1921 free(idx_objects);
1922
1923 if (!verify)
1924 final(pack_name, curr_pack,
1925 index_name, curr_index,
1926 + rev_index_name, curr_rev_index,
1927 keep_msg, promisor_msg,
1928 pack_hash);
1929 else
@@ -1893,10 +1934,13 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
1934
1935 free(objects);
1936 strbuf_release(&index_name_buf);
1937 + strbuf_release(&rev_index_name_buf);
1938 if (pack_name == NULL)
1939 free((void *) curr_pack);
1940 if (index_name == NULL)
1941 free((void *) curr_index);
1942 + if (rev_index_name == NULL)
1943 + free((void *) curr_rev_index);
1944
1945 /*
1946 * Let the caller know this pack is not self contained
t/t5325-reverse-index.sh new
+71
@@ -0,0 +1,71 @@
1 +#!/bin/sh
2 +
3 +test_description='on-disk reverse index'
4 +. ./test-lib.sh
5 +
6 +packdir=.git/objects/pack
7 +
8 +test_expect_success 'setup' '
9 + test_commit base &&
10 +
11 + pack=$(git pack-objects --all $packdir/pack) &&
12 + rev=$packdir/pack-$pack.rev &&
13 +
14 + test_path_is_missing $rev
15 +'
16 +
17 +test_index_pack () {
18 + rm -f $rev &&
19 + conf=$1 &&
20 + shift &&
21 + # remove the index since Windows won't overwrite an existing file
22 + rm $packdir/pack-$pack.idx &&
23 + git -c pack.writeReverseIndex=$conf index-pack "$@" \
24 + $packdir/pack-$pack.pack
25 +}
26 +
27 +test_expect_success 'index-pack with pack.writeReverseIndex' '
28 + test_index_pack "" &&
29 + test_path_is_missing $rev &&
30 +
31 + test_index_pack false &&
32 + test_path_is_missing $rev &&
33 +
34 + test_index_pack true &&
35 + test_path_is_file $rev
36 +'
37 +
38 +test_expect_success 'index-pack with --[no-]rev-index' '
39 + for conf in "" true false
40 + do
41 + test_index_pack "$conf" --rev-index &&
42 + test_path_exists $rev &&
43 +
44 + test_index_pack "$conf" --no-rev-index &&
45 + test_path_is_missing $rev
46 + done
47 +'
48 +
49 +test_expect_success 'index-pack can verify reverse indexes' '
50 + test_when_finished "rm -f $rev" &&
51 + test_index_pack true &&
52 +
53 + test_path_is_file $rev &&
54 + git index-pack --rev-index --verify $packdir/pack-$pack.pack &&
55 +
56 + # Intentionally corrupt the reverse index.
57 + chmod u+w $rev &&
58 + printf "xxxx" | dd of=$rev bs=1 count=4 conv=notrunc &&
59 +
60 + test_must_fail git index-pack --rev-index --verify \
61 + $packdir/pack-$pack.pack 2>err &&
62 + grep "validation error" err
63 +'
64 +
65 +test_expect_success 'index-pack infers reverse index name with -o' '
66 + git index-pack --rev-index -o other.idx $packdir/pack-$pack.pack &&
67 + test_path_is_file other.idx &&
68 + test_path_is_file other.rev
69 +'
70 +
71 +test_done