Skip to content

Commit 48eee46

Browse files
committed
Merge branch 'en/sparse-checkout'
"sparse-checkout" UI improvements. * en/sparse-checkout: sparse-checkout: provide a new reapply subcommand unpack-trees: failure to set SKIP_WORKTREE bits always just a warning unpack-trees: provide warnings on sparse updates for unmerged paths too unpack-trees: make sparse path messages sound like warnings unpack-trees: split display_error_msgs() into two unpack-trees: rename ERROR_* fields meant for warnings to WARNING_* unpack-trees: move ERROR_WOULD_LOSE_SUBMODULE earlier sparse-checkout: use improved unpack_trees porcelain messages sparse-checkout: use new update_sparsity() function unpack-trees: add a new update_sparsity() function unpack-trees: pull sparse-checkout pattern reading into a new function unpack-trees: do not mark a dirty path with SKIP_WORKTREE unpack-trees: allow check_updates() to work on a different index t1091: make some tests a little more defensive against failures unpack-trees: simplify pattern_list freeing unpack-trees: simplify verify_absent_sparse() unpack-trees: remove unused error type unpack-trees: fix minor typo in comment
2 parents 8cb514d + 5644ca2 commit 48eee46

File tree

7 files changed

+375
-111
lines changed

7 files changed

+375
-111
lines changed

Documentation/git-sparse-checkout.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ C-style quoted strings.
7070
`core.sparseCheckoutCone` is enabled, the given patterns are interpreted
7171
as directory names as in the 'set' subcommand.
7272

73+
'reapply::
74+
Reapply the sparsity pattern rules to paths in the working tree.
75+
Commands like merge or rebase can materialize paths to do their
76+
work (e.g. in order to show you a conflict), and other
77+
sparse-checkout commands might fail to sparsify an individual file
78+
(e.g. because it has unstaged changes or conflicts). In such
79+
cases, it can make sense to run `git sparse-checkout reapply` later
80+
after cleaning up affected paths (e.g. resolving conflicts, undoing
81+
or committing changes, etc.).
82+
7383
'disable'::
7484
Disable the `core.sparseCheckout` config setting, and restore the
7585
working directory to include all files. Leaves the sparse-checkout

builtin/sparse-checkout.c

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
static const char *empty_base = "";
1919

2020
static char const * const builtin_sparse_checkout_usage[] = {
21-
N_("git sparse-checkout (init|list|set|add|disable) <options>"),
21+
N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"),
2222
NULL
2323
};
2424

@@ -94,50 +94,37 @@ static int sparse_checkout_list(int argc, const char **argv)
9494

9595
static int update_working_directory(struct pattern_list *pl)
9696
{
97-
int result = 0;
97+
enum update_sparsity_result result;
9898
struct unpack_trees_options o;
9999
struct lock_file lock_file = LOCK_INIT;
100-
struct object_id oid;
101-
struct tree *tree;
102-
struct tree_desc t;
103100
struct repository *r = the_repository;
104101

105-
if (repo_read_index_unmerged(r))
106-
die(_("you need to resolve your current index first"));
107-
108-
if (get_oid("HEAD", &oid))
109-
return 0;
110-
111-
tree = parse_tree_indirect(&oid);
112-
parse_tree(tree);
113-
init_tree_desc(&t, tree->buffer, tree->size);
114-
115102
memset(&o, 0, sizeof(o));
116103
o.verbose_update = isatty(2);
117-
o.merge = 1;
118104
o.update = 1;
119-
o.fn = oneway_merge;
120105
o.head_idx = -1;
121106
o.src_index = r->index;
122107
o.dst_index = r->index;
123108
o.skip_sparse_checkout = 0;
124109
o.pl = pl;
125-
o.keep_pattern_list = !!pl;
126110

127-
resolve_undo_clear_index(r->index);
128111
setup_work_tree();
129112

130-
cache_tree_free(&r->index->cache_tree);
131-
132113
repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
133114

134-
core_apply_sparse_checkout = 1;
135-
result = unpack_trees(1, &t, &o);
136-
137-
if (!result) {
138-
prime_cache_tree(r, r->index, tree);
115+
setup_unpack_trees_porcelain(&o, "sparse-checkout");
116+
result = update_sparsity(&o);
117+
clear_unpack_trees_porcelain(&o);
118+
119+
if (result == UPDATE_SPARSITY_WARNINGS)
120+
/*
121+
* We don't do any special handling of warnings from untracked
122+
* files in the way or dirty entries that can't be removed.
123+
*/
124+
result = UPDATE_SPARSITY_SUCCESS;
125+
if (result == UPDATE_SPARSITY_SUCCESS)
139126
write_locked_index(r->index, &lock_file, COMMIT_LOCK);
140-
} else
127+
else
141128
rollback_lock_file(&lock_file);
142129

143130
return result;
@@ -304,8 +291,6 @@ static int sparse_checkout_init(int argc, const char **argv)
304291
};
305292

306293
repo_read_index(the_repository);
307-
require_clean_work_tree(the_repository,
308-
N_("initialize sparse-checkout"), NULL, 1, 0);
309294

310295
argc = parse_options(argc, argv, NULL,
311296
builtin_sparse_checkout_init_options,
@@ -560,8 +545,6 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
560545
};
561546

562547
repo_read_index(the_repository);
563-
require_clean_work_tree(the_repository,
564-
N_("set sparse-checkout patterns"), NULL, 1, 0);
565548

566549
argc = parse_options(argc, argv, prefix,
567550
builtin_sparse_checkout_set_options,
@@ -571,14 +554,18 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
571554
return modify_pattern_list(argc, argv, m);
572555
}
573556

557+
static int sparse_checkout_reapply(int argc, const char **argv)
558+
{
559+
repo_read_index(the_repository);
560+
return update_working_directory(NULL);
561+
}
562+
574563
static int sparse_checkout_disable(int argc, const char **argv)
575564
{
576565
struct pattern_list pl;
577566
struct strbuf match_all = STRBUF_INIT;
578567

579568
repo_read_index(the_repository);
580-
require_clean_work_tree(the_repository,
581-
N_("disable sparse-checkout"), NULL, 1, 0);
582569

583570
memset(&pl, 0, sizeof(pl));
584571
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
@@ -622,6 +609,8 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
622609
return sparse_checkout_set(argc, argv, prefix, REPLACE);
623610
if (!strcmp(argv[0], "add"))
624611
return sparse_checkout_set(argc, argv, prefix, ADD);
612+
if (!strcmp(argv[0], "reapply"))
613+
return sparse_checkout_reapply(argc, argv);
625614
if (!strcmp(argv[0], "disable"))
626615
return sparse_checkout_disable(argc, argv);
627616
}

t/t1011-read-tree-sparse-checkout.sh

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,18 +233,19 @@ test_expect_success 'read-tree --reset removes outside worktree' '
233233
test_must_be_empty result
234234
'
235235

236-
test_expect_success 'print errors when failed to update worktree' '
236+
test_expect_success 'print warnings when some worktree updates disabled' '
237237
echo sub >.git/info/sparse-checkout &&
238238
git checkout -f init &&
239239
mkdir sub &&
240240
touch sub/added sub/addedtoo &&
241-
test_must_fail git checkout top 2>actual &&
241+
# Use -q to suppress "Previous HEAD position" and "Head is now at" msgs
242+
git checkout -q top 2>actual &&
242243
cat >expected <<\EOF &&
243-
error: The following untracked working tree files would be overwritten by checkout:
244+
warning: The following paths were already present and thus not updated despite sparse patterns:
244245
sub/added
245246
sub/addedtoo
246-
Please move or remove them before you switch branches.
247-
Aborting
247+
248+
After fixing the above paths, you may want to run `git sparse-checkout reapply`.
248249
EOF
249250
test_i18ncmp expected actual
250251
'

t/t1091-sparse-checkout-builtin.sh

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -277,15 +277,23 @@ test_expect_success 'cone mode: add parent path' '
277277
check_files repo a deep folder1
278278
'
279279

280-
test_expect_success 'revert to old sparse-checkout on bad update' '
280+
test_expect_success 'not-up-to-date does not block rest of sparsification' '
281+
test_when_finished git -C repo sparse-checkout disable &&
281282
test_when_finished git -C repo reset --hard &&
282283
git -C repo sparse-checkout set deep &&
284+
283285
echo update >repo/deep/deeper2/a &&
284286
cp repo/.git/info/sparse-checkout expect &&
285-
test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
286-
test_i18ngrep "cannot set sparse-checkout patterns" err &&
287-
test_cmp repo/.git/info/sparse-checkout expect &&
288-
check_files repo/deep a deeper1 deeper2
287+
test_write_lines "!/deep/*/" "/deep/deeper1/" >>expect &&
288+
289+
git -C repo sparse-checkout set deep/deeper1 2>err &&
290+
291+
test_i18ngrep "The following paths are not up to date" err &&
292+
test_cmp expect repo/.git/info/sparse-checkout &&
293+
check_files repo/deep a deeper1 deeper2 &&
294+
check_files repo/deep/deeper1 a deepest &&
295+
check_files repo/deep/deeper1/deepest a &&
296+
check_files repo/deep/deeper2 a
289297
'
290298

291299
test_expect_success 'revert to old sparse-checkout on empty update' '
@@ -315,19 +323,96 @@ test_expect_success '.gitignore should not warn about cone mode' '
315323
test_i18ngrep ! "disabling cone patterns" err
316324
'
317325

318-
test_expect_success 'sparse-checkout (init|set|disable) fails with dirty status' '
326+
test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
319327
git clone repo dirty &&
320328
echo dirty >dirty/folder1/a &&
321-
test_must_fail git -C dirty sparse-checkout init &&
322-
test_must_fail git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
323-
test_must_fail git -C dirty sparse-checkout disable &&
329+
330+
git -C dirty sparse-checkout init 2>err &&
331+
test_i18ngrep "warning.*The following paths are not up to date" err &&
332+
333+
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
334+
test_i18ngrep "warning.*The following paths are not up to date" err &&
335+
test_path_is_file dirty/folder1/a &&
336+
337+
git -C dirty sparse-checkout disable 2>err &&
338+
test_must_be_empty err &&
339+
324340
git -C dirty reset --hard &&
325341
git -C dirty sparse-checkout init &&
326342
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
327-
git -C dirty sparse-checkout disable
343+
test_path_is_missing dirty/folder1/a &&
344+
git -C dirty sparse-checkout disable &&
345+
test_path_is_file dirty/folder1/a
346+
'
347+
348+
test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged status' '
349+
git clone repo unmerged &&
350+
351+
cat >input <<-EOF &&
352+
0 0000000000000000000000000000000000000000 folder1/a
353+
100644 $(git -C unmerged rev-parse HEAD:folder1/a) 1 folder1/a
354+
EOF
355+
git -C unmerged update-index --index-info <input &&
356+
357+
git -C unmerged sparse-checkout init 2>err &&
358+
test_i18ngrep "warning.*The following paths are unmerged" err &&
359+
360+
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
361+
test_i18ngrep "warning.*The following paths are unmerged" err &&
362+
test_path_is_file dirty/folder1/a &&
363+
364+
git -C unmerged sparse-checkout disable 2>err &&
365+
test_i18ngrep "warning.*The following paths are unmerged" err &&
366+
367+
git -C unmerged reset --hard &&
368+
git -C unmerged sparse-checkout init &&
369+
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* &&
370+
git -C unmerged sparse-checkout disable
371+
'
372+
373+
test_expect_success 'sparse-checkout reapply' '
374+
git clone repo tweak &&
375+
376+
echo dirty >tweak/deep/deeper2/a &&
377+
378+
cat >input <<-EOF &&
379+
0 0000000000000000000000000000000000000000 folder1/a
380+
100644 $(git -C tweak rev-parse HEAD:folder1/a) 1 folder1/a
381+
EOF
382+
git -C tweak update-index --index-info <input &&
383+
384+
git -C tweak sparse-checkout init --cone 2>err &&
385+
test_i18ngrep "warning.*The following paths are not up to date" err &&
386+
test_i18ngrep "warning.*The following paths are unmerged" err &&
387+
388+
git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
389+
test_i18ngrep "warning.*The following paths are not up to date" err &&
390+
test_i18ngrep "warning.*The following paths are unmerged" err &&
391+
392+
git -C tweak sparse-checkout reapply 2>err &&
393+
test_i18ngrep "warning.*The following paths are not up to date" err &&
394+
test_path_is_file tweak/deep/deeper2/a &&
395+
test_i18ngrep "warning.*The following paths are unmerged" err &&
396+
test_path_is_file tweak/folder1/a &&
397+
398+
git -C tweak checkout HEAD deep/deeper2/a &&
399+
git -C tweak sparse-checkout reapply 2>err &&
400+
test_i18ngrep ! "warning.*The following paths are not up to date" err &&
401+
test_path_is_missing tweak/deep/deeper2/a &&
402+
test_i18ngrep "warning.*The following paths are unmerged" err &&
403+
test_path_is_file tweak/folder1/a &&
404+
405+
git -C tweak add folder1/a &&
406+
git -C tweak sparse-checkout reapply 2>err &&
407+
test_must_be_empty err &&
408+
test_path_is_missing tweak/deep/deeper2/a &&
409+
test_path_is_missing tweak/folder1/a &&
410+
411+
git -C tweak sparse-checkout disable
328412
'
329413

330414
test_expect_success 'cone mode: set with core.ignoreCase=true' '
415+
rm repo/.git/info/sparse-checkout &&
331416
git -C repo sparse-checkout init --cone &&
332417
git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
333418
cat >expect <<-\EOF &&

t/t2018-checkout-branch.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,26 @@ test_expect_success 'checkout -b after clone --no-checkout does a checkout of HE
238238
test_path_is_file dest/a.t
239239
'
240240

241+
test_expect_success 'checkout -b to a new branch preserves mergeable changes despite sparse-checkout' '
242+
test_when_finished "
243+
git reset --hard &&
244+
git checkout branch1-scratch &&
245+
test_might_fail git branch -D branch3 &&
246+
git config core.sparseCheckout false &&
247+
rm .git/info/sparse-checkout" &&
248+
249+
test_commit file2 &&
250+
251+
echo stuff >>file1 &&
252+
echo file2 >.git/info/sparse-checkout &&
253+
git config core.sparseCheckout true &&
254+
255+
CURHEAD=$(git rev-parse HEAD) &&
256+
do_checkout branch3 $CURHEAD &&
257+
258+
echo file1 >expect &&
259+
git diff --name-only >actual &&
260+
test_cmp expect actual
261+
'
262+
241263
test_done

0 commit comments

Comments
 (0)