Merge branch 'ds/sparse-cone'

Management of sparsely checked-out working tree has gained a
dedicated "sparse-checkout" command.

* ds/sparse-cone: (21 commits)
  sparse-checkout: improve OS ls compatibility
  sparse-checkout: respect core.ignoreCase in cone mode
  sparse-checkout: check for dirty status
  sparse-checkout: update working directory in-process for 'init'
  sparse-checkout: cone mode should not interact with .gitignore
  sparse-checkout: write using lockfile
  sparse-checkout: use in-process update for disable subcommand
  sparse-checkout: update working directory in-process
  sparse-checkout: sanitize for nested folders
  unpack-trees: add progress to clear_ce_flags()
  unpack-trees: hash less in cone mode
  sparse-checkout: init and set in cone mode
  sparse-checkout: use hashmaps for cone patterns
  sparse-checkout: add 'cone' mode
  trace2: add region in clear_ce_flags
  sparse-checkout: create 'disable' subcommand
  sparse-checkout: add '--stdin' option to set subcommand
  sparse-checkout: 'set' subcommand
  clone: add --sparse mode
  sparse-checkout: create 'init' subcommand
  ...
This commit is contained in:
Junio C Hamano
2019-12-25 11:21:58 -08:00
20 changed files with 1375 additions and 47 deletions

View File

@ -1269,7 +1269,8 @@ static int clear_ce_flags_1(struct index_state *istate,
struct strbuf *prefix,
int select_mask, int clear_mask,
struct pattern_list *pl,
enum pattern_match_result default_match);
enum pattern_match_result default_match,
int progress_nr);
/* Whole directory matching */
static int clear_ce_flags_dir(struct index_state *istate,
@ -1278,20 +1279,23 @@ static int clear_ce_flags_dir(struct index_state *istate,
char *basename,
int select_mask, int clear_mask,
struct pattern_list *pl,
enum pattern_match_result default_match)
enum pattern_match_result default_match,
int progress_nr)
{
struct cache_entry **cache_end;
int dtype = DT_DIR;
int rc;
enum pattern_match_result ret;
ret = path_matches_pattern_list(prefix->buf, prefix->len,
basename, &dtype, pl, istate);
enum pattern_match_result ret, orig_ret;
orig_ret = path_matches_pattern_list(prefix->buf, prefix->len,
basename, &dtype, pl, istate);
strbuf_addch(prefix, '/');
/* If undecided, use matching result of parent dir in defval */
if (ret == UNDECIDED)
if (orig_ret == UNDECIDED)
ret = default_match;
else
ret = orig_ret;
for (cache_end = cache; cache_end != cache + nr; cache_end++) {
struct cache_entry *ce = *cache_end;
@ -1299,17 +1303,24 @@ static int clear_ce_flags_dir(struct index_state *istate,
break;
}
/*
* TODO: check pl, if there are no patterns that may conflict
* with ret (iow, we know in advance the incl/excl
* decision for the entire directory), clear flag here without
* calling clear_ce_flags_1(). That function will call
* the expensive path_matches_pattern_list() on every entry.
*/
rc = clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask,
pl, ret);
if (pl->use_cone_patterns && orig_ret == MATCHED_RECURSIVE) {
struct cache_entry **ce = cache;
rc = (cache_end - cache) / sizeof(struct cache_entry *);
while (ce < cache_end) {
(*ce)->ce_flags &= ~clear_mask;
ce++;
}
} else if (pl->use_cone_patterns && orig_ret == NOT_MATCHED) {
rc = (cache_end - cache) / sizeof(struct cache_entry *);
} else {
rc = clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask,
pl, ret,
progress_nr);
}
strbuf_setlen(prefix, prefix->len - 1);
return rc;
}
@ -1334,7 +1345,8 @@ static int clear_ce_flags_1(struct index_state *istate,
struct strbuf *prefix,
int select_mask, int clear_mask,
struct pattern_list *pl,
enum pattern_match_result default_match)
enum pattern_match_result default_match,
int progress_nr)
{
struct cache_entry **cache_end = cache + nr;
@ -1348,8 +1360,11 @@ static int clear_ce_flags_1(struct index_state *istate,
int len, dtype;
enum pattern_match_result ret;
display_progress(istate->progress, progress_nr);
if (select_mask && !(ce->ce_flags & select_mask)) {
cache++;
progress_nr++;
continue;
}
@ -1370,20 +1385,26 @@ static int clear_ce_flags_1(struct index_state *istate,
prefix,
prefix->buf + prefix->len - len,
select_mask, clear_mask,
pl, default_match);
pl, default_match,
progress_nr);
/* clear_c_f_dir eats a whole dir already? */
if (processed) {
cache += processed;
progress_nr += processed;
strbuf_setlen(prefix, prefix->len - len);
continue;
}
strbuf_addch(prefix, '/');
cache += clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask, pl,
default_match);
processed = clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask, pl,
default_match, progress_nr);
cache += processed;
progress_nr += processed;
strbuf_setlen(prefix, prefix->len - len - 1);
continue;
}
@ -1398,24 +1419,41 @@ static int clear_ce_flags_1(struct index_state *istate,
if (ret == MATCHED)
ce->ce_flags &= ~clear_mask;
cache++;
progress_nr++;
}
display_progress(istate->progress, progress_nr);
return nr - (cache_end - cache);
}
static int clear_ce_flags(struct index_state *istate,
int select_mask, int clear_mask,
struct pattern_list *pl)
struct pattern_list *pl,
int show_progress)
{
static struct strbuf prefix = STRBUF_INIT;
char label[100];
int rval;
strbuf_reset(&prefix);
if (show_progress)
istate->progress = start_delayed_progress(
_("Updating index flags"),
istate->cache_nr);
return clear_ce_flags_1(istate,
xsnprintf(label, sizeof(label), "clear_ce_flags(0x%08lx,0x%08lx)",
(unsigned long)select_mask, (unsigned long)clear_mask);
trace2_region_enter("unpack_trees", label, the_repository);
rval = clear_ce_flags_1(istate,
istate->cache,
istate->cache_nr,
&prefix,
select_mask, clear_mask,
pl, 0);
pl, 0, 0);
trace2_region_leave("unpack_trees", label, the_repository);
stop_progress(&istate->progress);
return rval;
}
/*
@ -1423,7 +1461,8 @@ static int clear_ce_flags(struct index_state *istate,
*/
static void mark_new_skip_worktree(struct pattern_list *pl,
struct index_state *istate,
int select_flag, int skip_wt_flag)
int select_flag, int skip_wt_flag,
int show_progress)
{
int i;
@ -1447,7 +1486,7 @@ static void mark_new_skip_worktree(struct pattern_list *pl,
* 2. Widen worktree according to sparse-checkout file.
* Matched entries will have skip_wt_flag cleared (i.e. "in")
*/
clear_ce_flags(istate, select_flag, skip_wt_flag, pl);
clear_ce_flags(istate, select_flag, skip_wt_flag, pl, show_progress);
}
static int verify_absent(const struct cache_entry *,
@ -1472,8 +1511,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
memset(&pl, 0, sizeof(pl));
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
if (!o->skip_sparse_checkout) {
if (!o->skip_sparse_checkout && !o->pl) {
char *sparse = git_pathdup("info/sparse-checkout");
pl.use_cone_patterns = core_sparse_checkout_cone;
if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
o->skip_sparse_checkout = 1;
else
@ -1511,7 +1551,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
* Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
*/
if (!o->skip_sparse_checkout)
mark_new_skip_worktree(o->pl, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
mark_new_skip_worktree(o->pl, o->src_index, 0,
CE_NEW_SKIP_WORKTREE, o->verbose_update);
if (!dfc)
dfc = xcalloc(1, cache_entry_size(0));
@ -1576,7 +1617,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
* If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
* so apply_sparse_checkout() won't attempt to remove it from worktree
*/
mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
mark_new_skip_worktree(o->pl, &o->result,
CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE,
o->verbose_update);
ret = 0;
for (i = 0; i < o->result.cache_nr; i++) {
@ -1644,7 +1687,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
done:
trace_performance_leave("unpack_trees");
clear_pattern_list(&pl);
if (!o->keep_pattern_list)
clear_pattern_list(&pl);
return ret;
return_failed: