Update `checkout-index` to no longer refresh files that have the
`skip-worktree` bit set, exiting with an error if `skip-worktree` filenames
are directly provided to `checkout-index`. The newly-added
`--ignore-skip-worktree-bits` option provides a mechanism to replicate the
old behavior, checking out *all* files specified (even those with
`skip-worktree` enabled).
The ability to toggle whether files should be checked-out based on
`skip-worktree` already exists in `git checkout` and `git restore` (both of
which have an `--ignore-skip-worktree-bits` option). The change to, by
default, ignore `skip-worktree` files is especially helpful for
sparse-checkout; it prevents inadvertent creation of files outside the
sparse definition on disk and eliminates the need to expand a sparse index
when using the `--all` option.
Internal usage of `checkout-index` in `git stash` and `git filter-branch` do
not make explicit use of files with `skip-worktree` enabled, so
`--ignore-skip-worktree-bits` is not added to them.
Helped-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Victoria Dye <vdye@github.com>
Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Victoria Dye committedJan 11, 2022 at 18:05 UTC88078f543b769dc13ae9796372651178584a25a0
3 files changed+38-12
Documentation/git-checkout-index.txt
+8-2
index 4d33e7be0f..01dbd5cbf5 100644--- a/Documentation/git-checkout-index.txt+++ b/Documentation/git-checkout-index.txt@@ -12,6 +12,7 @@ SYNOPSIS 'git checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>] [--stage=<number>|all] [--temp]+ [--ignore-skip-worktree-bits] [-z] [--stdin] [--] [<file>...]@@ -37,8 +38,9 @@ OPTIONS -a:: --all::- checks out all files in the index. Cannot be used- together with explicit filenames.+ checks out all files in the index except for those with the+ skip-worktree bit set (see `--ignore-skip-worktree-bits`).+ Cannot be used together with explicit filenames. -n:: --no-create::@@ -59,6 +61,10 @@ OPTIONS write the content to temporary files. The temporary name associations will be written to stdout.+--ignore-skip-worktree-bits::+ Check out all files, including those with the skip-worktree bit+ set.+ --stdin:: Instead of taking list of paths from the command line, read list of paths from the standard input. Paths are
builtin/checkout-index.c
+13
index e21620d964..615a118e2f 100644--- a/builtin/checkout-index.c+++ b/builtin/checkout-index.c@@ -7,6 +7,7 @@ #define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h"+#include "dir.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h"@@ -17,6 +18,7 @@ #define CHECKOUT_ALL 4 static int nul_term_line; static int checkout_stage; /* default to checkout stage0 */+static int ignore_skip_worktree; /* default to 0 */ static int to_tempfile; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];@@ -65,6 +67,7 @@ static int checkout_file(const char *name, const char *prefix) int namelen = strlen(name); int pos = cache_name_pos(name, namelen); int has_same_name = 0;+ int is_skipped = 1; int did_checkout = 0; int errs = 0;@@ -78,6 +81,9 @@ static int checkout_file(const char *name, const char *prefix) break; has_same_name = 1; pos++;+ if (!ignore_skip_worktree && ce_skip_worktree(ce))+ break;+ is_skipped = 0; if (ce_stage(ce) != checkout_stage && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) continue;@@ -106,6 +112,9 @@ static int checkout_file(const char *name, const char *prefix) fprintf(stderr, "git checkout-index: %s ", name); if (!has_same_name) fprintf(stderr, "is not in the cache");+ else if (is_skipped)+ fprintf(stderr, "has skip-worktree enabled; "+ "use '--ignore-skip-worktree-bits' to checkout"); else if (checkout_stage) fprintf(stderr, "does not exist at stage %d", checkout_stage);@@ -125,6 +134,8 @@ static int checkout_all(const char *prefix, int prefix_length) ensure_full_index(&the_index); for (i = 0; i < active_nr ; i++) { struct cache_entry *ce = active_cache[i];+ if (!ignore_skip_worktree && ce_skip_worktree(ce))+ continue; if (ce_stage(ce) != checkout_stage && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) continue;@@ -185,6 +196,8 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) struct option builtin_checkout_index_options[] = { OPT_BOOL('a', "all", &all, N_("check out all files in the index")),+ OPT_BOOL(0, "ignore-skip-worktree-bits", &ignore_skip_worktree,+ N_("do not skip files with skip-worktree set")), OPT__FORCE(&force, N_("force overwrite of existing files"), 0), OPT__QUIET(&quiet, N_("no warning for existing files and files not in index")),
t/t1092-sparse-checkout-compatibility.sh
+17-10
index db7ad41109..434ef0433c 100755--- a/t/t1092-sparse-checkout-compatibility.sh+++ b/t/t1092-sparse-checkout-compatibility.sh@@ -772,9 +772,14 @@ test_expect_success 'checkout-index inside sparse definition' ' test_expect_success 'checkout-index outside sparse definition' ' init_repos &&- # File does not exist on disk yet for sparse checkouts, so checkout-index- # succeeds without -f- test_sparse_match git checkout-index -- folder1/a &&+ # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger+ # an error+ test_sparse_match test_must_fail git checkout-index -- folder1/a &&+ test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err &&+ test_path_is_missing folder1/a &&++ # With --ignore-skip-worktree-bits, outside-of-cone files are checked out+ test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a && test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && test_cmp sparse-checkout/folder1/a full-checkout/folder1/a &&@@ -783,8 +788,8 @@ test_expect_success 'checkout-index outside sparse definition' ' run_on_sparse mkdir -p folder1 && run_on_all cp ../new-a folder1/a &&- test_all_match test_must_fail git checkout-index -- folder1/a &&- test_all_match git checkout-index -f -- folder1/a &&+ test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a &&+ test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a && test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && test_cmp sparse-checkout/folder1/a full-checkout/folder1/a '@@ -799,14 +804,16 @@ test_expect_success 'checkout-index with folders' ' test_all_match test_must_fail git checkout-index -f -- folder1/ '-# NEEDSWORK: even in sparse checkouts, checkout-index --all will create all-# files (even those outside the sparse definition) on disk. However, these files-# don't appear in the percentage of tracked files in git status.-test_expect_failure 'checkout-index --all' '+test_expect_success 'checkout-index --all' ' init_repos && test_all_match git checkout-index --all &&- test_sparse_match test_path_is_missing folder1+ test_sparse_match test_path_is_missing folder1 &&++ # --ignore-skip-worktree-bits will cause `skip-worktree` files to be+ # checked out, causing the outside-of-cone `folder1` to exist on-disk+ test_all_match git checkout-index --ignore-skip-worktree-bits --all &&+ test_all_match test_path_exists folder1 ' test_expect_success 'clean' '