Skip to content

Commit 8230107

Browse files
committed
Merge branch 'jt/bulk-prefetch'
"git read-tree" had a codepath where blobs are fetched one-by-one from the promisor remote, which has been corrected to fetch in bulk. * jt/bulk-prefetch: cache-tree: prefetch in partial clone read-tree unpack-trees: refactor prefetching code
2 parents e9fe413 + d3da223 commit 8230107

File tree

5 files changed

+82
-21
lines changed

5 files changed

+82
-21
lines changed

cache-tree.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ int cache_tree_fully_valid(struct cache_tree *it)
237237
return 1;
238238
}
239239

240+
static int must_check_existence(const struct cache_entry *ce)
241+
{
242+
return !(has_promisor_remote() && ce_skip_worktree(ce));
243+
}
244+
240245
static int update_one(struct cache_tree *it,
241246
struct cache_entry **cache,
242247
int entries,
@@ -378,8 +383,7 @@ static int update_one(struct cache_tree *it,
378383
}
379384

380385
ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
381-
(has_promisor_remote() &&
382-
ce_skip_worktree(ce));
386+
!must_check_existence(ce);
383387
if (is_null_oid(oid) ||
384388
(!ce_missing_ok && !has_object_file(oid))) {
385389
strbuf_release(&buffer);
@@ -466,6 +470,9 @@ int cache_tree_update(struct index_state *istate, int flags)
466470
if (!istate->cache_tree)
467471
istate->cache_tree = cache_tree();
468472

473+
if (!(flags & WRITE_TREE_MISSING_OK) && has_promisor_remote())
474+
prefetch_cache_entries(istate, must_check_existence);
475+
469476
trace_performance_enter();
470477
trace2_region_enter("cache_tree", "update", the_repository);
471478
i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,

cache.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,15 @@ struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_s
410410
*/
411411
void validate_cache_entries(const struct index_state *istate);
412412

413+
/*
414+
* Bulk prefetch all missing cache entries that are not GITLINKs and that match
415+
* the given predicate. This function should only be called if
416+
* has_promisor_remote() returns true.
417+
*/
418+
typedef int (*must_prefetch_predicate)(const struct cache_entry *);
419+
void prefetch_cache_entries(const struct index_state *istate,
420+
must_prefetch_predicate must_prefetch);
421+
413422
#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
414423
extern struct index_state the_index;
415424

read-cache.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "progress.h"
2828
#include "sparse-index.h"
2929
#include "csum-file.h"
30+
#include "promisor-remote.h"
3031

3132
/* Mask for the name length in ce_flags in the on-disk index */
3233

@@ -3663,3 +3664,25 @@ static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_ta
36633664
strbuf_add(sb, &buffer, sizeof(uint32_t));
36643665
}
36653666
}
3667+
3668+
void prefetch_cache_entries(const struct index_state *istate,
3669+
must_prefetch_predicate must_prefetch)
3670+
{
3671+
int i;
3672+
struct oid_array to_fetch = OID_ARRAY_INIT;
3673+
3674+
for (i = 0; i < istate->cache_nr; i++) {
3675+
struct cache_entry *ce = istate->cache[i];
3676+
3677+
if (S_ISGITLINK(ce->ce_mode) || !must_prefetch(ce))
3678+
continue;
3679+
if (!oid_object_info_extended(the_repository, &ce->oid,
3680+
NULL,
3681+
OBJECT_INFO_FOR_PREFETCH))
3682+
continue;
3683+
oid_array_append(&to_fetch, &ce->oid);
3684+
}
3685+
promisor_remote_get_direct(the_repository,
3686+
to_fetch.oid, to_fetch.nr);
3687+
oid_array_clear(&to_fetch);
3688+
}

t/t1022-read-tree-partial-clone.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/sh
2+
3+
test_description='git read-tree in partial clones'
4+
5+
TEST_NO_CREATE_REPO=1
6+
7+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
8+
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
9+
10+
. ./test-lib.sh
11+
12+
test_expect_success 'read-tree in partial clone prefetches in one batch' '
13+
test_when_finished "rm -rf server client trace" &&
14+
15+
git init server &&
16+
echo foo >server/one &&
17+
echo bar >server/two &&
18+
git -C server add one two &&
19+
git -C server commit -m "initial commit" &&
20+
TREE=$(git -C server rev-parse HEAD^{tree}) &&
21+
22+
git -C server config uploadpack.allowfilter 1 &&
23+
git -C server config uploadpack.allowanysha1inwant 1 &&
24+
git clone --bare --filter=blob:none "file://$(pwd)/server" client &&
25+
GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE &&
26+
27+
# "done" marks the end of negotiation (once per fetch). Expect that
28+
# only one fetch occurs.
29+
grep "fetch> done" trace >donelines &&
30+
test_line_count = 1 donelines
31+
'
32+
33+
test_done

unpack-trees.c

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ static void report_collided_checkout(struct index_state *index)
392392
string_list_clear(&list, 0);
393393
}
394394

395+
static int must_checkout(const struct cache_entry *ce)
396+
{
397+
return ce->ce_flags & CE_UPDATE;
398+
}
399+
395400
static int check_updates(struct unpack_trees_options *o,
396401
struct index_state *index)
397402
{
@@ -442,28 +447,12 @@ static int check_updates(struct unpack_trees_options *o,
442447
if (should_update_submodules())
443448
load_gitmodules_file(index, &state);
444449

445-
if (has_promisor_remote()) {
450+
if (has_promisor_remote())
446451
/*
447452
* Prefetch the objects that are to be checked out in the loop
448453
* below.
449454
*/
450-
struct oid_array to_fetch = OID_ARRAY_INIT;
451-
for (i = 0; i < index->cache_nr; i++) {
452-
struct cache_entry *ce = index->cache[i];
453-
454-
if (!(ce->ce_flags & CE_UPDATE) ||
455-
S_ISGITLINK(ce->ce_mode))
456-
continue;
457-
if (!oid_object_info_extended(the_repository, &ce->oid,
458-
NULL,
459-
OBJECT_INFO_FOR_PREFETCH))
460-
continue;
461-
oid_array_append(&to_fetch, &ce->oid);
462-
}
463-
promisor_remote_get_direct(the_repository,
464-
to_fetch.oid, to_fetch.nr);
465-
oid_array_clear(&to_fetch);
466-
}
455+
prefetch_cache_entries(index, must_checkout);
467456

468457
get_parallel_checkout_configs(&pc_workers, &pc_threshold);
469458

@@ -473,7 +462,7 @@ static int check_updates(struct unpack_trees_options *o,
473462
for (i = 0; i < index->cache_nr; i++) {
474463
struct cache_entry *ce = index->cache[i];
475464

476-
if (ce->ce_flags & CE_UPDATE) {
465+
if (must_checkout(ce)) {
477466
size_t last_pc_queue_size = pc_queue_size();
478467

479468
if (ce->ce_flags & CE_WT_REMOVE)

0 commit comments

Comments
 (0)