Skip to content

Commit e0a0195

Browse files
committed
Merge branch 'jt/repack-local-promisor' into seen
* jt/repack-local-promisor: index-pack: repack local links into promisor packs t5300: move --window clamp test next to unclamped t0410: use from-scratch server t0410: make test description clearer pack-objects: make variable non-static
2 parents e534b9e + 26ff004 commit e0a0195

File tree

6 files changed

+180
-12
lines changed

6 files changed

+180
-12
lines changed

Documentation/git-index-pack.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ include::object-format-disclaimer.txt[]
139139
written. If a `<message>` is provided, then that content will be
140140
written to the .promisor file for future reference. See
141141
link:technical/partial-clone.html[partial clone] for more information.
142+
+
143+
Also, if there are objects in the given pack that references non-promisor
144+
objects (in the repo), repacks those non-promisor objects into a promisor
145+
pack. This avoids a situation in which a repo has non-promisor objects that are
146+
accessible through promisor objects.
142147

143148
NOTES
144149
-----

builtin/index-pack.c

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "csum-file.h"
1010
#include "blob.h"
1111
#include "commit.h"
12+
#include "tag.h"
1213
#include "tree.h"
1314
#include "progress.h"
1415
#include "fsck.h"
@@ -20,9 +21,14 @@
2021
#include "object-file.h"
2122
#include "object-store-ll.h"
2223
#include "oid-array.h"
24+
#include "oidset.h"
25+
#include "path.h"
2326
#include "replace-object.h"
27+
#include "tree-walk.h"
2428
#include "promisor-remote.h"
29+
#include "run-command.h"
2530
#include "setup.h"
31+
#include "strvec.h"
2632

2733
static const char index_pack_usage[] =
2834
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -148,6 +154,13 @@ static uint32_t input_crc32;
148154
static int input_fd, output_fd;
149155
static const char *curr_pack;
150156

157+
/*
158+
* local_links is guarded by read_mutex, and record_local_links is read-only in
159+
* a thread.
160+
*/
161+
static struct oidset local_links = OIDSET_INIT;
162+
static int record_local_links;
163+
151164
static struct thread_local *thread_data;
152165
static int nr_dispatched;
153166
static int threads_active;
@@ -799,6 +812,44 @@ static int check_collison(struct object_entry *entry)
799812
return 0;
800813
}
801814

815+
static void record_if_local_object(const struct object_id *oid)
816+
{
817+
struct object_info info = OBJECT_INFO_INIT;
818+
if (oid_object_info_extended(the_repository, oid, &info, 0))
819+
/* Missing; assume it is a promisor object */
820+
return;
821+
if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor)
822+
return;
823+
oidset_insert(&local_links, oid);
824+
}
825+
826+
static void do_record_local_links(struct object *obj)
827+
{
828+
if (obj->type == OBJ_TREE) {
829+
struct tree *tree = (struct tree *)obj;
830+
struct tree_desc desc;
831+
struct name_entry entry;
832+
if (init_tree_desc_gently(&desc, &tree->object.oid,
833+
tree->buffer, tree->size, 0))
834+
/*
835+
* Error messages are given when packs are
836+
* verified, so do not print any here.
837+
*/
838+
return;
839+
while (tree_entry_gently(&desc, &entry))
840+
record_if_local_object(&entry.oid);
841+
} else if (obj->type == OBJ_COMMIT) {
842+
struct commit *commit = (struct commit *) obj;
843+
struct commit_list *parents = commit->parents;
844+
845+
for (; parents; parents = parents->next)
846+
record_if_local_object(&parents->item->object.oid);
847+
} else if (obj->type == OBJ_TAG) {
848+
struct tag *tag = (struct tag *) obj;
849+
record_if_local_object(get_tagged_oid(tag));
850+
}
851+
}
852+
802853
static void sha1_object(const void *data, struct object_entry *obj_entry,
803854
unsigned long size, enum object_type type,
804855
const struct object_id *oid)
@@ -845,7 +896,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
845896
free(has_data);
846897
}
847898

848-
if (strict || do_fsck_object) {
899+
if (strict || do_fsck_object || record_local_links) {
849900
read_lock();
850901
if (type == OBJ_BLOB) {
851902
struct blob *blob = lookup_blob(the_repository, oid);
@@ -877,6 +928,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
877928
die(_("fsck error in packed object"));
878929
if (strict && fsck_walk(obj, NULL, &fsck_options))
879930
die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
931+
if (record_local_links)
932+
do_record_local_links(obj);
880933

881934
if (obj->type == OBJ_TREE) {
882935
struct tree *item = (struct tree *) obj;
@@ -1719,6 +1772,57 @@ static void show_pack_info(int stat_only)
17191772
free(chain_histogram);
17201773
}
17211774

1775+
static void repack_local_links(void)
1776+
{
1777+
struct child_process cmd = CHILD_PROCESS_INIT;
1778+
FILE *out;
1779+
struct strbuf line = STRBUF_INIT;
1780+
struct oidset_iter iter;
1781+
struct object_id *oid;
1782+
char *base_name;
1783+
1784+
if (!oidset_size(&local_links))
1785+
return;
1786+
1787+
base_name = mkpathdup("%s/pack/pack", repo_get_object_directory(the_repository));
1788+
1789+
strvec_push(&cmd.args, "pack-objects");
1790+
strvec_push(&cmd.args, "--exclude-promisor-objects-best-effort");
1791+
strvec_push(&cmd.args, base_name);
1792+
cmd.git_cmd = 1;
1793+
cmd.in = -1;
1794+
cmd.out = -1;
1795+
if (start_command(&cmd))
1796+
die(_("could not start pack-objects to repack local links"));
1797+
1798+
oidset_iter_init(&local_links, &iter);
1799+
while ((oid = oidset_iter_next(&iter))) {
1800+
if (write_in_full(cmd.in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
1801+
write_in_full(cmd.in, "\n", 1) < 0)
1802+
die(_("failed to feed local object to pack-objects"));
1803+
}
1804+
close(cmd.in);
1805+
1806+
out = xfdopen(cmd.out, "r");
1807+
while (strbuf_getline_lf(&line, out) != EOF) {
1808+
unsigned char binary[GIT_MAX_RAWSZ];
1809+
if (line.len != the_hash_algo->hexsz ||
1810+
!hex_to_bytes(binary, line.buf, line.len))
1811+
die(_("index-pack: Expecting full hex object ID lines only from pack-objects."));
1812+
1813+
/*
1814+
* pack-objects creates the .pack and .idx files, but not the
1815+
* .promisor file. Create the .promisor file, which is empty.
1816+
*/
1817+
write_special_file("promisor", "", NULL, binary, NULL);
1818+
}
1819+
1820+
fclose(out);
1821+
if (finish_command(&cmd))
1822+
die(_("could not finish pack-objects to repack local links"));
1823+
strbuf_release(&line);
1824+
}
1825+
17221826
int cmd_index_pack(int argc,
17231827
const char **argv,
17241828
const char *prefix,
@@ -1794,7 +1898,7 @@ int cmd_index_pack(int argc,
17941898
} else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
17951899
; /* nothing to do */
17961900
} else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
1797-
; /* already parsed */
1901+
record_local_links = 1;
17981902
} else if (starts_with(arg, "--threads=")) {
17991903
char *end;
18001904
nr_threads = strtoul(arg+10, &end, 0);
@@ -1970,6 +2074,8 @@ int cmd_index_pack(int argc,
19702074
free((void *) curr_index);
19712075
free(curr_rev_index);
19722076

2077+
repack_local_links();
2078+
19732079
/*
19742080
* Let the caller know this pack is not self contained
19752081
*/

builtin/pack-objects.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,6 @@ static enum {
248248
} write_bitmap_index;
249249
static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;
250250

251-
static int exclude_promisor_objects;
252-
253251
static int use_delta_islands;
254252

255253
static unsigned long delta_cache_size = 0;
@@ -4620,6 +4618,18 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED,
46204618
return 0;
46214619
}
46224620

4621+
static int should_include_obj(struct object *obj, void *data UNUSED)
4622+
{
4623+
struct object_info info = OBJECT_INFO_INIT;
4624+
if (oid_object_info_extended(the_repository, &obj->oid, &info, 0))
4625+
BUG("should_include_obj should only be called on existing objects");
4626+
return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor;
4627+
}
4628+
4629+
static int should_include(struct commit *commit, void *data) {
4630+
return should_include_obj((struct object *) commit, data);
4631+
}
4632+
46234633
int cmd_pack_objects(int argc,
46244634
const char **argv,
46254635
const char *prefix,
@@ -4635,6 +4645,8 @@ int cmd_pack_objects(int argc,
46354645
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
46364646
struct list_objects_filter_options filter_options =
46374647
LIST_OBJECTS_FILTER_INIT;
4648+
int exclude_promisor_objects = 0;
4649+
int exclude_promisor_objects_best_effort = 0;
46384650

46394651
struct option pack_objects_options[] = {
46404652
OPT_CALLBACK_F('q', "quiet", &progress, NULL,
@@ -4734,6 +4746,9 @@ int cmd_pack_objects(int argc,
47344746
option_parse_missing_action),
47354747
OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
47364748
N_("do not pack objects in promisor packfiles")),
4749+
OPT_BOOL(0, "exclude-promisor-objects-best-effort",
4750+
&exclude_promisor_objects_best_effort,
4751+
N_("implies --missing=allow-any")),
47374752
OPT_BOOL(0, "delta-islands", &use_delta_islands,
47384753
N_("respect islands during delta compression")),
47394754
OPT_STRING_LIST(0, "uri-protocol", &uri_protocols,
@@ -4845,10 +4860,18 @@ int cmd_pack_objects(int argc,
48454860
strvec_push(&rp, "--unpacked");
48464861
}
48474862

4863+
if (exclude_promisor_objects && exclude_promisor_objects_best_effort)
4864+
die(_("options '%s' and '%s' cannot be used together"),
4865+
"--exclude-promisor-objects", "--exclude-promisor-objects-best-effort");
48484866
if (exclude_promisor_objects) {
48494867
use_internal_rev_list = 1;
48504868
fetch_if_missing = 0;
48514869
strvec_push(&rp, "--exclude-promisor-objects");
4870+
} else if (exclude_promisor_objects_best_effort) {
4871+
use_internal_rev_list = 1;
4872+
fetch_if_missing = 0;
4873+
option_parse_missing_action(NULL, "allow-any", 0);
4874+
/* revs configured below */
48524875
}
48534876
if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
48544877
use_internal_rev_list = 1;
@@ -4968,6 +4991,10 @@ int cmd_pack_objects(int argc,
49684991

49694992
repo_init_revisions(the_repository, &revs, NULL);
49704993
list_objects_filter_copy(&revs.filter, &filter_options);
4994+
if (exclude_promisor_objects_best_effort) {
4995+
revs.include_check = should_include;
4996+
revs.include_check_obj = should_include_obj;
4997+
}
49714998
get_object_list(&revs, rp.nr, rp.v);
49724999
release_revisions(&revs);
49735000
}

t/t0410-partial-clone.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
241241
grep "fetch< fetch=.*ref-in-want" trace
242242
'
243243

244-
test_expect_success 'fetching of missing objects from another promisor remote' '
244+
test_expect_success 'fetching from another promisor remote' '
245245
git clone "file://$(pwd)/server" server2 &&
246246
test_commit -C server2 bar &&
247247
git -C server2 repack -a -d --write-bitmap-index &&
@@ -264,8 +264,8 @@ test_expect_success 'fetching of missing objects from another promisor remote' '
264264
grep "$HASH2" out
265265
'
266266

267-
test_expect_success 'fetching of missing objects configures a promisor remote' '
268-
git clone "file://$(pwd)/server" server3 &&
267+
test_expect_success 'fetching with --filter configures a promisor remote' '
268+
test_create_repo server3 &&
269269
test_commit -C server3 baz &&
270270
git -C server3 repack -a -d --write-bitmap-index &&
271271
HASH3=$(git -C server3 rev-parse baz) &&

t/t5300-pack-object.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ test_expect_success 'pack without delta' '
156156
check_deltas stderr = 0
157157
'
158158

159+
test_expect_success 'negative window clamps to 0' '
160+
git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
161+
check_deltas stderr = 0
162+
'
163+
159164
test_expect_success 'pack-objects with bogus arguments' '
160165
test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list
161166
'
@@ -630,11 +635,6 @@ test_expect_success 'prefetch objects' '
630635
test_line_count = 1 donelines
631636
'
632637

633-
test_expect_success 'negative window clamps to 0' '
634-
git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
635-
check_deltas stderr = 0
636-
'
637-
638638
for hash in sha1 sha256
639639
do
640640
test_expect_success "verify-pack with $hash packfile" '

t/t5616-partial-clone.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,36 @@ test_expect_success 'lazy-fetch in submodule succeeds' '
694694
git -C client restore --recurse-submodules --source=HEAD^ :/
695695
'
696696

697+
test_expect_success 'after fetching descendants of non-promisor commits, gc works' '
698+
# Setup
699+
git init full &&
700+
git -C full config uploadpack.allowfilter 1 &&
701+
git -C full config uploadpack.allowanysha1inwant 1 &&
702+
touch full/foo &&
703+
git -C full add foo &&
704+
git -C full commit -m "commit 1" &&
705+
git -C full checkout --detach &&
706+
707+
# Partial clone and push commit to remote
708+
git clone "file://$(pwd)/full" --filter=blob:none partial &&
709+
echo "hello" > partial/foo &&
710+
git -C partial commit -a -m "commit 2" &&
711+
git -C partial push &&
712+
713+
# gc in partial repo
714+
git -C partial gc --prune=now &&
715+
716+
# Create another commit in normal repo
717+
git -C full checkout main &&
718+
echo " world" >> full/foo &&
719+
git -C full commit -a -m "commit 3" &&
720+
721+
# Pull from remote in partial repo, and run gc again
722+
git -C partial pull &&
723+
git -C partial gc --prune=now
724+
'
725+
726+
697727
. "$TEST_DIRECTORY"/lib-httpd.sh
698728
start_httpd
699729

0 commit comments

Comments
 (0)