Skip to content

Commit 532e216

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: refactor skip worktree retry logic
The clear_skip_worktree_from_present_files() method was introduced in af6a518 (repo_read_index: clear SKIP_WORKTREE bit from files present in worktree, 2022-01-14) to help cases where sparse-checkout is enabled but some paths outside of the sparse-checkout also exist on disk. This operation can be slow as it needs to check path existence in a way not stored in the index, so caching was introduced in d79d299 (Accelerate clear_skip_worktree_from_present_files() by caching, 2022-01-14). This check is particularly confusing in the presence of a sparse index, as a sparse tree entry corresponding to an existing directory must first be expanded to a full index before examining the paths within. This is currently implemented using a 'goto' and a boolean variable to ensure we restart only once. Even with that caching, it was noticed that this could take a long time to execute. 89aaab1 (index: add trace2 region for clear skip worktree, 2022-11-03) introduced trace2 regions to measure this time. Further, the way the loop repeats itself was slightly confusing and prone to breakage, so a BUG() statement was added in 8c7abdc (index: raise a bug if the index is materialised more than once, 2022-11-03) to be sure that the second run of the loop does not hit any sparse trees. One thing that can be confusing about the current setup is that the trace2 regions nest and it is not clear that a second loop is running after a sparse index is expanded. Here is an example of what the regions look like in a typical case: | region_enter | ... | label:clear_skip_worktree_from_present_files | region_enter | ... | ..label:update | region_leave | ... | ..label:update | region_enter | ... | ..label:ensure_full_index | region_enter | ... | ....label:update | region_leave | ... | ....label:update | region_leave | ... | ..label:ensure_full_index | data | ... | ..sparse_path_count:1 | data | ... | ..sparse_path_count_full:269538 | region_leave | ... | label:clear_skip_worktree_from_present_files One thing that is particularly difficult to understand about these regions is that most of the time is spent between the close of the ensure_full_index region and the reporting of the end data. This is because of the restart of the loop being within the same region as the first iteration of the loop. This change refactors the method into two separate methods that are traced separately. This will be more important later when we change other features of the methods, but for now the only functional change is the difference in the structure of the trace regions. After this change, the same telemetry section is split into three distinct chunks: | region_enter | ... | label:clear_skip_worktree_from_present_files_sparse | data | ... | ..sparse_path_count:1 | region_leave | ... | label:clear_skip_worktree_from_present_files_sparse | region_enter | ... | label:update | region_leave | ... | label:update | region_enter | ... | label:ensure_full_index | region_enter | ... | ..label:update | region_leave | ... | ..label:update | region_leave | ... | label:ensure_full_index | region_enter | ... | label:clear_skip_worktree_from_present_files_full | data | ... | ..full_path_count:269538 | region_leave | ... | label:clear_skip_worktree_from_present_files_full Here, we see the sparse loop terminating early with its first sparse path being a sparse directory containing a file. Then, that loop's region terminates before ensure_full_index begins (in this case, the cache-tree must also be computed). Then, _after_ the index is expanded, the full loop begins with its own region. Signed-off-by: Derrick Stolee <[email protected]> Reviewed-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 66ac6e4 commit 532e216

File tree

1 file changed

+53
-24
lines changed

1 file changed

+53
-24
lines changed

sparse-index.c

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -486,49 +486,78 @@ static int path_found(const char *path, const char **dirname, size_t *dir_len,
486486
return 0;
487487
}
488488

489-
void clear_skip_worktree_from_present_files(struct index_state *istate)
489+
static int clear_skip_worktree_from_present_files_sparse(struct index_state *istate)
490490
{
491491
const char *last_dirname = NULL;
492492
size_t dir_len = 0;
493493
int dir_found = 1;
494494

495-
int i;
496-
int path_count[2] = {0, 0};
497-
int restarted = 0;
495+
int path_count = 0;
496+
int to_restart = 0;
498497

499-
if (!core_apply_sparse_checkout ||
500-
sparse_expect_files_outside_of_patterns)
501-
return;
502-
503-
trace2_region_enter("index", "clear_skip_worktree_from_present_files",
498+
trace2_region_enter("index", "clear_skip_worktree_from_present_files_sparse",
504499
istate->repo);
505-
restart:
506-
for (i = 0; i < istate->cache_nr; i++) {
500+
for (int i = 0; i < istate->cache_nr; i++) {
507501
struct cache_entry *ce = istate->cache[i];
508502

509503
if (ce_skip_worktree(ce)) {
510-
path_count[restarted]++;
504+
path_count++;
511505
if (path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {
512506
if (S_ISSPARSEDIR(ce->ce_mode)) {
513-
if (restarted)
514-
BUG("ensure-full-index did not fully flatten?");
515-
ensure_full_index(istate);
516-
restarted = 1;
517-
goto restart;
507+
to_restart = 1;
508+
break;
518509
}
519510
ce->ce_flags &= ~CE_SKIP_WORKTREE;
520511
}
521512
}
522513
}
523514

524-
if (path_count[0])
525-
trace2_data_intmax("index", istate->repo,
526-
"sparse_path_count", path_count[0]);
527-
if (restarted)
528-
trace2_data_intmax("index", istate->repo,
529-
"sparse_path_count_full", path_count[1]);
530-
trace2_region_leave("index", "clear_skip_worktree_from_present_files",
515+
trace2_data_intmax("index", istate->repo,
516+
"sparse_path_count", path_count);
517+
trace2_region_leave("index", "clear_skip_worktree_from_present_files_sparse",
518+
istate->repo);
519+
return to_restart;
520+
}
521+
522+
static void clear_skip_worktree_from_present_files_full(struct index_state *istate)
523+
{
524+
const char *last_dirname = NULL;
525+
size_t dir_len = 0;
526+
int dir_found = 1;
527+
528+
int path_count = 0;
529+
530+
trace2_region_enter("index", "clear_skip_worktree_from_present_files_full",
531531
istate->repo);
532+
for (int i = 0; i < istate->cache_nr; i++) {
533+
struct cache_entry *ce = istate->cache[i];
534+
535+
if (S_ISSPARSEDIR(ce->ce_mode))
536+
BUG("ensure-full-index did not fully flatten?");
537+
538+
if (ce_skip_worktree(ce)) {
539+
path_count++;
540+
if (path_found(ce->name, &last_dirname, &dir_len, &dir_found))
541+
ce->ce_flags &= ~CE_SKIP_WORKTREE;
542+
}
543+
}
544+
545+
trace2_data_intmax("index", istate->repo,
546+
"full_path_count", path_count);
547+
trace2_region_leave("index", "clear_skip_worktree_from_present_files_full",
548+
istate->repo);
549+
}
550+
551+
void clear_skip_worktree_from_present_files(struct index_state *istate)
552+
{
553+
if (!core_apply_sparse_checkout ||
554+
sparse_expect_files_outside_of_patterns)
555+
return;
556+
557+
if (clear_skip_worktree_from_present_files_sparse(istate)) {
558+
ensure_full_index(istate);
559+
clear_skip_worktree_from_present_files_full(istate);
560+
}
532561
}
533562

534563
/*

0 commit comments

Comments
 (0)