Skip to content

Commit 0299a69

Browse files
derrickstoleegitster
authored andcommitted
add: implement the --sparse option
We previously modified 'git add' to refuse updating index entries outside of the sparse-checkout cone. This is justified to prevent users from accidentally getting into a confusing state when Git removes those files from the working tree at some later point. Unfortunately, this caused some workflows that were previously possible to become impossible, especially around merge conflicts outside of the sparse-checkout cone. These were documented in tests within t1092. We now re-enable these workflows using a new '--sparse' option to 'git add'. This allows users to signal "Yes, I do know what I'm doing with these files," and accept the consequences of the files leaving the worktree later. We delay updating the advice message until implementing a similar option in 'git rm' and 'git mv'. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 49fdd51 commit 0299a69

File tree

4 files changed

+43
-24
lines changed

4 files changed

+43
-24
lines changed

Documentation/git-add.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ SYNOPSIS
99
--------
1010
[verse]
1111
'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
12-
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
12+
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse]
1313
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
1414
[--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
1515
[--] [<pathspec>...]
@@ -79,6 +79,13 @@ in linkgit:gitglossary[7].
7979
--force::
8080
Allow adding otherwise ignored files.
8181

82+
--sparse::
83+
Allow updating index entries outside of the sparse-checkout cone.
84+
Normally, `git add` refuses to update index entries whose paths do
85+
not fit within the sparse-checkout cone, since those files might
86+
be removed from the working tree without warning. See
87+
linkgit:git-sparse-checkout[1] for more details.
88+
8289
-i::
8390
--interactive::
8491
Add modified contents in the working tree interactively to

builtin/add.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static int patch_interactive, add_interactive, edit_interactive;
3030
static int take_worktree_changes;
3131
static int add_renormalize;
3232
static int pathspec_file_nul;
33+
static int include_sparse;
3334
static const char *pathspec_from_file;
3435
static int legacy_stash_p; /* support for the scripted `git stash` */
3536

@@ -46,7 +47,7 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
4647
struct cache_entry *ce = active_cache[i];
4748
int err;
4849

49-
if (ce_skip_worktree(ce))
50+
if (!include_sparse && ce_skip_worktree(ce))
5051
continue;
5152

5253
if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
@@ -95,7 +96,7 @@ static void update_callback(struct diff_queue_struct *q,
9596
struct diff_filepair *p = q->queue[i];
9697
const char *path = p->one->path;
9798

98-
if (!path_in_sparse_checkout(path, &the_index))
99+
if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
99100
continue;
100101

101102
switch (fix_unmerged_status(p, data)) {
@@ -383,6 +384,7 @@ static struct option builtin_add_options[] = {
383384
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
384385
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
385386
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
387+
OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
386388
OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x",
387389
N_("override the executable bit of the listed files")),
388390
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
@@ -461,7 +463,8 @@ static int add_files(struct dir_struct *dir, int flags)
461463
}
462464

463465
for (i = 0; i < dir->nr; i++) {
464-
if (!path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
466+
if (!include_sparse &&
467+
!path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
465468
string_list_append(&matched_sparse_paths,
466469
dir->entries[i]->name);
467470
continue;
@@ -646,7 +649,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
646649
if (seen[i])
647650
continue;
648651

649-
if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
652+
if (!include_sparse &&
653+
matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
650654
string_list_append(&only_match_skip_worktree,
651655
pathspec.items[i].original);
652656
continue;

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,7 @@ test_expect_success 'commit including unstaged changes' '
343343
test_all_match git status --porcelain=v2
344344
'
345345

346-
# NEEDSWORK: Now that 'git add folder1/new' fails, the changes being
347-
# attempted here fail for the sparse-checkout and sparse-index repos.
348-
# We must enable a way for adding files outside the sparse-checkout
349-
# done, even if it is by an optional flag.
350-
test_expect_failure 'status/add: outside sparse cone' '
346+
test_expect_success 'status/add: outside sparse cone' '
351347
init_repos &&
352348
353349
# folder1 is at HEAD, but outside the sparse cone
@@ -375,15 +371,16 @@ test_expect_failure 'status/add: outside sparse cone' '
375371
test_sparse_match test_must_fail git add folder1/new &&
376372
grep "Disable or modify the sparsity rules" sparse-checkout-err &&
377373
test_sparse_unstaged folder1/new &&
374+
test_sparse_match git add --sparse folder1/a &&
375+
test_sparse_match git add --sparse folder1/new &&
378376
379-
# NEEDSWORK: behavior begins to deviate here.
380-
test_all_match git add . &&
377+
test_all_match git add --sparse . &&
381378
test_all_match git status --porcelain=v2 &&
382379
test_all_match git commit -m folder1/new &&
383380
test_all_match git rev-parse HEAD^{tree} &&
384381
385382
run_on_all ../edit-contents folder1/newer &&
386-
test_all_match git add folder1/ &&
383+
test_all_match git add --sparse folder1/ &&
387384
test_all_match git status --porcelain=v2 &&
388385
test_all_match git commit -m folder1/newer &&
389386
test_all_match git rev-parse HEAD^{tree}
@@ -527,12 +524,7 @@ test_expect_success 'merge, cherry-pick, and rebase' '
527524
done
528525
'
529526

530-
# NEEDSWORK: This test is documenting current behavior, but that
531-
# behavior can be confusing to users so there is desire to change it.
532-
# Right now, users might be using this flow to work through conflicts,
533-
# so any solution should present advice to users who try this sequence
534-
# of commands to follow whatever new method we create.
535-
test_expect_failure 'merge with conflict outside cone' '
527+
test_expect_success 'merge with conflict outside cone' '
536528
init_repos &&
537529
538530
test_all_match git checkout -b merge-tip merge-left &&
@@ -549,25 +541,24 @@ test_expect_failure 'merge with conflict outside cone' '
549541
test_sparse_match test_must_fail git add folder1/a &&
550542
grep "Disable or modify the sparsity rules" sparse-checkout-err &&
551543
test_sparse_unstaged folder1/a &&
544+
test_all_match git add --sparse folder1/a &&
552545
test_all_match git status --porcelain=v2 &&
553546
554547
# 3. Rename the file to another sparse filename and
555548
# accept conflict markers as resolved content.
556549
run_on_all mv folder2/a folder2/z &&
557-
# NEEDSWORK: This mode now fails, because folder2/z is
558-
# outside of the sparse-checkout cone and does not match an
559-
# existing index entry with the SKIP_WORKTREE bit cleared.
560550
test_sparse_match test_must_fail git add folder2 &&
561551
grep "Disable or modify the sparsity rules" sparse-checkout-err &&
562552
test_sparse_unstaged folder2/z &&
553+
test_all_match git add --sparse folder2 &&
563554
test_all_match git status --porcelain=v2 &&
564555
565556
test_all_match git merge --continue &&
566557
test_all_match git status --porcelain=v2 &&
567558
test_all_match git rev-parse HEAD^{tree}
568559
'
569560

570-
test_expect_failure 'cherry-pick/rebase with conflict outside cone' '
561+
test_expect_success 'cherry-pick/rebase with conflict outside cone' '
571562
init_repos &&
572563
573564
for OPERATION in cherry-pick rebase
@@ -590,6 +581,7 @@ test_expect_failure 'cherry-pick/rebase with conflict outside cone' '
590581
test_sparse_match test_must_fail git add folder1/a &&
591582
grep "Disable or modify the sparsity rules" sparse-checkout-err &&
592583
test_sparse_unstaged folder1/a &&
584+
test_all_match git add --sparse folder1/a &&
593585
test_all_match git status --porcelain=v2 &&
594586
595587
# 3. Rename the file to another sparse filename and
@@ -601,6 +593,7 @@ test_expect_failure 'cherry-pick/rebase with conflict outside cone' '
601593
test_sparse_match test_must_fail git add folder2 &&
602594
grep "Disable or modify the sparsity rules" sparse-checkout-err &&
603595
test_sparse_unstaged folder2/z &&
596+
test_all_match git add --sparse folder2 &&
604597
test_all_match git status --porcelain=v2 &&
605598
606599
test_all_match git $OPERATION --continue &&

t/t3705-add-sparse-checkout.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,13 @@ test_expect_success 'git add fails outside of sparse-checkout definition' '
167167
168168
git update-index --no-skip-worktree sparse_entry &&
169169
test_must_fail git add sparse_entry &&
170-
test_sparse_entry_unstaged
170+
test_sparse_entry_unstaged &&
171+
172+
# Avoid munging CRLFs to avoid an error message
173+
git -c core.autocrlf=input add --sparse sparse_entry 2>stderr &&
174+
test_must_be_empty stderr &&
175+
test-tool read-cache --table >actual &&
176+
grep "^100644 blob.*sparse_entry\$" actual
171177
'
172178

173179
test_expect_success 'add obeys advice.updateSparsePath' '
@@ -178,4 +184,13 @@ test_expect_success 'add obeys advice.updateSparsePath' '
178184
179185
'
180186

187+
test_expect_success 'add allows sparse entries with --sparse' '
188+
git sparse-checkout set a &&
189+
echo modified >sparse_entry &&
190+
test_must_fail git add sparse_entry &&
191+
test_sparse_entry_unchanged &&
192+
git add --sparse sparse_entry 2>stderr &&
193+
test_must_be_empty stderr
194+
'
195+
181196
test_done

0 commit comments

Comments
 (0)