diff --git a/Documentation/config.txt b/Documentation/config.txt index bf3e512921f..f0fb25a371c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -503,6 +503,8 @@ include::config/sequencer.txt[] include::config/showbranch.txt[] +include::config/sparse.txt[] + include::config/splitindex.txt[] include::config/ssh.txt[] diff --git a/Documentation/config/sparse.txt b/Documentation/config/sparse.txt new file mode 100644 index 00000000000..aff49a8d3a3 --- /dev/null +++ b/Documentation/config/sparse.txt @@ -0,0 +1,27 @@ +sparse.expectFilesOutsideOfPatterns:: + Typically with sparse checkouts, files not matching any + sparsity patterns are marked with a SKIP_WORKTREE bit in the + index and are missing from the working tree. Accordingly, Git + will ordinarily check whether files with the SKIP_WORKTREE bit + are in fact present in the working tree contrary to + expectations. If Git finds any, it marks those paths as + present by clearing the relevant SKIP_WORKTREE bits. This + option can be used to tell Git that such + present-despite-skipped files are expected and to stop + checking for them. ++ +The default is `false`, which allows Git to automatically recover +from the list of files in the index and working tree falling out of +sync. ++ +Set this to `true` if you are in a setup where some external factor +relieves Git of the responsibility for maintaining the consistency +between the presence of working tree files and sparsity patterns. For +example, if you have a Git-aware virtual file system that has a robust +mechanism for keeping the working tree and the sparsity patterns up to +date based on access patterns. ++ +Regardless of this setting, Git does not check for +present-despite-skipped files unless sparse checkout is enabled, so +this config option has no effect unless `core.sparseCheckout` is +`true`. diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 8c3aceb8324..99bb387134d 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -375,9 +375,14 @@ have finished your work-in-progress), attempt the merge again. SPARSE CHECKOUT --------------- +Note: The `update-index` and `read-tree` primitives for supporting the +skip-worktree bit predated the introduction of +linkgit:git-sparse-checkout[1]. Users are encouraged to use +`sparse-checkout` in preference to these low-level primitives. + "Sparse checkout" allows populating the working directory sparsely. -It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell -Git whether a file in the working directory is worth looking at. +It uses the skip-worktree bit (see linkgit:git-update-index[1]) to +tell Git whether a file in the working directory is worth looking at. 'git read-tree' and other merge-based commands ('git merge', 'git checkout'...) can help maintaining the skip-worktree bitmap and working @@ -385,7 +390,8 @@ directory update. `$GIT_DIR/info/sparse-checkout` is used to define the skip-worktree reference bitmap. When 'git read-tree' needs to update the working directory, it resets the skip-worktree bit in the index based on this file, which uses the same syntax as .gitignore files. -If an entry matches a pattern in this file, skip-worktree will not be +If an entry matches a pattern in this file, or the entry corresponds to +a file present in the working tree, then skip-worktree will not be set on that entry. Otherwise, skip-worktree will be set. Then it compares the new skip-worktree value with the previous one. If diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt index 94dad137b96..88e55f432f3 100644 --- a/Documentation/git-sparse-checkout.txt +++ b/Documentation/git-sparse-checkout.txt @@ -3,9 +3,7 @@ git-sparse-checkout(1) NAME ---- -git-sparse-checkout - Initialize and modify the sparse-checkout -configuration, which reduces the checkout to a set of paths -given by a list of patterns. +git-sparse-checkout - Reduce your working tree to a subset of tracked files SYNOPSIS @@ -17,8 +15,20 @@ SYNOPSIS DESCRIPTION ----------- -Initialize and modify the sparse-checkout configuration, which reduces -the checkout to a set of paths given by a list of patterns. +This command is used to create sparse checkouts, which means that it +changes the working tree from having all tracked files present, to only +have a subset of them. It can also switch which subset of files are +present, or undo and go back to having all tracked files present in the +working copy. + +The subset of files is chosen by providing a list of directories in +cone mode (which is recommended), or by providing a list of patterns +in non-cone mode. + +When in a sparse-checkout, other Git commands behave a bit differently. +For example, switching branches will not update paths outside the +sparse-checkout directories/patterns, and `git commit -a` will not record +paths outside the sparse-checkout directories/patterns as deleted. THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR, AND THE BEHAVIOR OF OTHER COMMANDS IN THE PRESENCE OF SPARSE-CHECKOUTS, WILL LIKELY CHANGE IN @@ -28,7 +38,7 @@ THE FUTURE. COMMANDS -------- 'list':: - Describe the patterns in the sparse-checkout file. + Describe the directories or patterns in the sparse-checkout file. 'set':: Enable the necessary sparse-checkout config settings @@ -46,20 +56,26 @@ the 'set' subcommand are stored in the worktree-specific sparse-checkout file. See linkgit:git-worktree[1] and the documentation of `extensions.worktreeConfig` in linkgit:git-config[1] for more details. + -When the `--stdin` option is provided, the patterns are read from -standard in as a newline-delimited list instead of from the arguments. +When the `--stdin` option is provided, the directories or patterns are +read from standard in as a newline-delimited list instead of from the +arguments. + When `--cone` is passed or `core.sparseCheckoutCone` is enabled, the -input list is considered a list of directories instead of -sparse-checkout patterns. This allows for better performance with a -limited set of patterns (see 'CONE PATTERN SET' below). Note that the -set command will write patterns to the sparse-checkout file to include -all files contained in those directories (recursively) as well as -files that are siblings of ancestor directories. The input format -matches the output of `git ls-tree --name-only`. This includes -interpreting pathnames that begin with a double quote (") as C-style -quoted strings. This may become the default in the future; --no-cone -can be passed to request non-cone mode. +input list is considered a list of directories. This allows for +better performance with a limited set of patterns (see 'CONE PATTERN +SET' below). The input format matches the output of `git ls-tree +--name-only`. This includes interpreting pathnames that begin with a +double quote (") as C-style quoted strings. Note that the set command +will write patterns to the sparse-checkout file to include all files +contained in those directories (recursively) as well as files that are +siblings of ancestor directories. This may become the default in the +future; --no-cone can be passed to request non-cone mode. ++ +When `--no-cone` is passed or `core.sparseCheckoutCone` is not enabled, +the input list is considered a list of patterns. This mode is harder +to use and less performant, and is thus not recommended. See the +"Sparse Checkout" section of linkgit:git-read-tree[1] and the "Pattern +Set" sections below for more details. + Use the `--[no-]sparse-index` option to use a sparse index (the default is to not use it). A sparse index reduces the size of the @@ -77,11 +93,10 @@ understand the sparse directory entries index extension and may fail to interact with your repository until it is disabled. 'add':: - Update the sparse-checkout file to include additional patterns. - By default, these patterns are read from the command-line arguments, - but they can be read from stdin using the `--stdin` option. When - `core.sparseCheckoutCone` is enabled, the given patterns are interpreted - as directory names as in the 'set' subcommand. + Update the sparse-checkout file to include additional directories + (in cone mode) or patterns (in non-cone mode). By default, these + directories or patterns are read from the command-line arguments, + but they can be read from stdin using the `--stdin` option. 'reapply':: Reapply the sparsity pattern rules to paths in the working tree. @@ -125,13 +140,14 @@ decreased in utility. SPARSE CHECKOUT --------------- -"Sparse checkout" allows populating the working directory sparsely. -It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell -Git whether a file in the working directory is worth looking at. If -the skip-worktree bit is set, then the file is ignored in the working -directory. Git will avoid populating the contents of those files, which -makes a sparse checkout helpful when working in a repository with many -files, but only a few are important to the current user. +"Sparse checkout" allows populating the working directory sparsely. It +uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell Git +whether a file in the working directory is worth looking at. If the +skip-worktree bit is set, and the file is not present in the working tree, +then its absence is ignored. Git will avoid populating the contents of +those files, which makes a sparse checkout helpful when working in a +repository with many files, but only a few are important to the current +user. The `$GIT_DIR/info/sparse-checkout` file is used to define the skip-worktree reference bitmap. When Git updates the working diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 2853f168d97..568dbfe76b8 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -351,6 +351,10 @@ unchanged". Note that "assume unchanged" bit is *not* set if the index (use `git update-index --really-refresh` if you want to mark them as "assume unchanged"). +Sometimes users confuse the assume-unchanged bit with the +skip-worktree bit. See the final paragraph in the "Skip-worktree bit" +section below for an explanation of the differences. + EXAMPLES -------- @@ -392,22 +396,47 @@ M foo.c SKIP-WORKTREE BIT ----------------- -Skip-worktree bit can be defined in one (long) sentence: When reading -an entry, if it is marked as skip-worktree, then Git pretends its -working directory version is up to date and read the index version -instead. +Skip-worktree bit can be defined in one (long) sentence: Tell git to +avoid writing the file to the working directory when reasonably +possible, and treat the file as unchanged when it is not +present in the working directory. -To elaborate, "reading" means checking for file existence, reading -file attributes or file content. The working directory version may be -present or absent. If present, its content may match against the index -version or not. Writing is not affected by this bit, content safety -is still first priority. Note that Git _can_ update working directory -file, that is marked skip-worktree, if it is safe to do so (i.e. -working directory version matches index version) +Note that not all git commands will pay attention to this bit, and +some only partially support it. + +The update-index flags and the read-tree capabilities relating to the +skip-worktree bit predated the introduction of the +linkgit:git-sparse-checkout[1] command, which provides a much easier +way to configure and handle the skip-worktree bits. If you want to +reduce your working tree to only deal with a subset of the files in +the repository, we strongly encourage the use of +linkgit:git-sparse-checkout[1] in preference to the low-level +update-index and read-tree primitives. + +The primary purpose of the skip-worktree bit is to enable sparse +checkouts, i.e. to have working directories with only a subset of +paths present. When the skip-worktree bit is set, Git commands (such +as `switch`, `pull`, `merge`) will avoid writing these files. +However, these commands will sometimes write these files anyway in +important cases such as conflicts during a merge or rebase. Git +commands will also avoid treating the lack of such files as an +intentional deletion; for example `git add -u` will not not stage a +deletion for these files and `git commit -a` will not make a commit +deleting them either. Although this bit looks similar to assume-unchanged bit, its goal is -different from assume-unchanged bit's. Skip-worktree also takes -precedence over assume-unchanged bit when both are set. +different. The assume-unchanged bit is for leaving the file in the +working tree but having Git omit checking it for changes and presuming +that the file has not been changed (though if it can determine without +stat'ing the file that it has changed, it is free to record the +changes). skip-worktree tells Git to ignore the absence of the file, +avoid updating it when possible with commands that normally update +much of the working directory (e.g. `checkout`, `switch`, `pull`, +etc.), and not have its absence be recorded in commits. Note that in +sparse checkouts (setup by `git sparse-checkout` or by configuring +core.sparseCheckout to true), if a file is marked as skip-worktree in +the index but is found in the working tree, Git will clear the +skip-worktree bit for that file. SPLIT INDEX ----------- diff --git a/cache.h b/cache.h index 04d4d2db25c..9e3d423b951 100644 --- a/cache.h +++ b/cache.h @@ -1003,6 +1003,7 @@ extern const char *core_fsmonitor; extern int core_apply_sparse_checkout; extern int core_sparse_checkout_cone; +extern int sparse_expect_files_outside_of_patterns; /* * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value). diff --git a/config.c b/config.c index 383b1a4885b..e78397725c9 100644 --- a/config.c +++ b/config.c @@ -1654,6 +1654,17 @@ static int git_default_core_config(const char *var, const char *value, void *cb) return platform_core_config(var, value, cb); } +static int git_default_sparse_config(const char *var, const char *value) +{ + if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) { + sparse_expect_files_outside_of_patterns = git_config_bool(var, value); + return 0; + } + + /* Add other config variables here and to Documentation/config/sparse.txt. */ + return 0; +} + static int git_default_i18n_config(const char *var, const char *value) { if (!strcmp(var, "i18n.commitencoding")) @@ -1785,6 +1796,9 @@ int git_default_config(const char *var, const char *value, void *cb) return 0; } + if (starts_with(var, "sparse.")) + return git_default_sparse_config(var, value); + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/environment.c b/environment.c index fd0501e77a5..fb55bf61290 100644 --- a/environment.c +++ b/environment.c @@ -70,6 +70,7 @@ char *notes_ref_name; int grafts_replace_parents = 1; int core_apply_sparse_checkout; int core_sparse_checkout_cone; +int sparse_expect_files_outside_of_patterns; int merge_log_config = -1; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ unsigned long pack_size_limit_cfg; diff --git a/repository.c b/repository.c index 34610c5a33e..dddee32258f 100644 --- a/repository.c +++ b/repository.c @@ -301,6 +301,13 @@ int repo_read_index(struct repository *repo) if (repo->settings.command_requires_full_index) ensure_full_index(repo->index); + /* + * If sparse checkouts are in use, check whether paths with the + * SKIP_WORKTREE attribute are missing from the worktree; if not, + * clear that attribute for that path. + */ + clear_skip_worktree_from_present_files(repo->index); + return res; } diff --git a/sparse-index.c b/sparse-index.c index fdbe97b9769..8636af72de5 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -337,6 +337,80 @@ void ensure_correct_sparsity(struct index_state *istate) ensure_full_index(istate); } +static int path_found(const char *path, const char **dirname, size_t *dir_len, + int *dir_found) +{ + struct stat st; + char *newdir; + char *tmp; + + /* + * If dirname corresponds to a directory that doesn't exist, and this + * path starts with dirname, then path can't exist. + */ + if (!*dir_found && !memcmp(path, *dirname, *dir_len)) + return 0; + + /* + * If path itself exists, return 1. + */ + if (!lstat(path, &st)) + return 1; + + /* + * Otherwise, path does not exist so we'll return 0...but we'll first + * determine some info about its parent directory so we can avoid + * lstat calls for future cache entries. + */ + newdir = strrchr(path, '/'); + if (!newdir) + return 0; /* Didn't find a parent dir; just return 0 now. */ + + /* + * If path starts with directory (which we already lstat'ed and found), + * then no need to lstat parent directory again. + */ + if (*dir_found && *dirname && memcmp(path, *dirname, *dir_len)) + return 0; + + /* Free previous dirname, and cache path's dirname */ + *dirname = path; + *dir_len = newdir - path + 1; + + tmp = xstrndup(path, *dir_len); + *dir_found = !lstat(tmp, &st); + free(tmp); + + return 0; +} + +void clear_skip_worktree_from_present_files(struct index_state *istate) +{ + const char *last_dirname = NULL; + size_t dir_len = 0; + int dir_found = 1; + + int i; + + if (!core_apply_sparse_checkout || + sparse_expect_files_outside_of_patterns) + return; + +restart: + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + + if (ce_skip_worktree(ce) && + path_found(ce->name, &last_dirname, &dir_len, &dir_found)) { + if (S_ISSPARSEDIR(ce->ce_mode)) { + ensure_full_index(istate); + goto restart; + } + ce->ce_flags &= ~CE_SKIP_WORKTREE; + } + } +} + /* * This static global helps avoid infinite recursion between * expand_to_path() and index_file_exists(). diff --git a/sparse-index.h b/sparse-index.h index 656bd835b25..633d4fb7e31 100644 --- a/sparse-index.h +++ b/sparse-index.h @@ -5,6 +5,7 @@ struct index_state; #define SPARSE_INDEX_MEMORY_ONLY (1 << 0) int convert_to_sparse(struct index_state *istate, int flags); void ensure_correct_sparsity(struct index_state *istate); +void clear_skip_worktree_from_present_files(struct index_state *istate); /* * Some places in the codebase expect to search for a specific path. diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 24092c09a95..dd957be1b78 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -187,11 +187,32 @@ test_expect_success 'read-tree updates worktree, absent case' ' test ! -f init.t ' +test_expect_success 'read-tree will not throw away dirty changes, non-sparse' ' + echo "/*" >.git/info/sparse-checkout && + read_tree_u_must_succeed -m -u HEAD && + + echo dirty >init.t && + read_tree_u_must_fail -m -u HEAD^ && + test_path_is_file init.t && + grep -q dirty init.t +' + +test_expect_success 'read-tree will not throw away dirty changes, sparse' ' + echo "/*" >.git/info/sparse-checkout && + read_tree_u_must_succeed -m -u HEAD && + + echo dirty >init.t && + echo sub/added >.git/info/sparse-checkout && + read_tree_u_must_fail -m -u HEAD^ && + test_path_is_file init.t && + grep -q dirty init.t +' + test_expect_success 'read-tree updates worktree, dirty case' ' echo sub/added >.git/info/sparse-checkout && git checkout -f top && echo dirty >init.t && - read_tree_u_must_succeed -m -u HEAD^ && + read_tree_u_must_fail -m -u HEAD^ && grep -q dirty init.t && rm init.t ' diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh index 3deb4901874..d1833c0f31b 100755 --- a/t/t1090-sparse-checkout-scope.sh +++ b/t/t1090-sparse-checkout-scope.sh @@ -52,6 +52,25 @@ test_expect_success 'return to full checkout of main' ' test "$(cat b)" = "modified" ' +test_expect_success 'skip-worktree on files outside sparse patterns' ' + git sparse-checkout disable && + git sparse-checkout set --no-cone "a*" && + git checkout-index --all --ignore-skip-worktree-bits && + + git ls-files -t >output && + ! grep ^S output >actual && + test_must_be_empty actual && + + test_config sparse.expectFilesOutsideOfPatterns true && + cat <<-\EOF >expect && + S b + S c + EOF + git ls-files -t >output && + grep ^S output >actual && + test_cmp expect actual +' + test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' ' test_create_repo server && git clone "file://$(pwd)/server" client && diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index f3a059e5af5..2a04b532f91 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -367,7 +367,7 @@ test_expect_success 'status/add: outside sparse cone' ' write_script edit-contents <<-\EOF && echo text >>$1 EOF - run_on_sparse ../edit-contents folder1/a && + run_on_all ../edit-contents folder1/a && run_on_all ../edit-contents folder1/new && test_sparse_match git status --porcelain=v2 && @@ -376,8 +376,8 @@ test_expect_success 'status/add: outside sparse cone' ' test_sparse_match test_must_fail git add folder1/a && grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && - test_sparse_match test_must_fail git add --refresh folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_all_match git add --refresh folder1/a && + test_must_be_empty sparse-checkout-err && test_sparse_unstaged folder1/a && test_sparse_match test_must_fail git add folder1/new && grep "Disable or modify the sparsity rules" sparse-checkout-err && @@ -643,11 +643,11 @@ test_expect_success 'update-index modify outside sparse definition' ' run_on_sparse cp ../initial-repo/folder1/a folder1/a && run_on_all ../edit-contents folder1/a && - # If file has skip-worktree enabled, update-index does not modify the - # index entry - test_sparse_match git update-index folder1/a && - test_sparse_match git status --porcelain=v2 && - test_must_be_empty sparse-checkout-out && + # If file has skip-worktree enabled, but the file is present, it is + # treated the same as if skip-worktree is disabled + test_all_match git status --porcelain=v2 && + test_all_match git update-index folder1/a && + test_all_match git status --porcelain=v2 && # When skip-worktree is disabled (even on files outside sparse cone), file # is updated in the index @@ -1331,30 +1331,27 @@ test_expect_success 'ls-files' ' test_cmp dense sparse && # Set up a strange condition of having a file edit - # outside of the sparse-checkout cone. This is just - # to verify that sparse-checkout and sparse-index - # behave the same in this case. + # outside of the sparse-checkout cone. We want to verify + # that all modes handle this the same, and detect the + # modification. write_script edit-content <<-\EOF && - mkdir folder1 && + mkdir -p folder1 && echo content >>folder1/a EOF - run_on_sparse ../edit-content && + run_on_all ../edit-content && - # ls-files does not currently notice modified files whose - # cache entries are marked SKIP_WORKTREE. This may change - # in the future, but here we test that sparse index does - # not accidentally create a change of behavior. - test_sparse_match git ls-files --modified && - test_must_be_empty sparse-checkout-out && - test_must_be_empty sparse-index-out && + test_all_match git ls-files --modified && git -C sparse-index ls-files --sparse --modified >sparse-index-out && - test_must_be_empty sparse-index-out && + cat >expect <<-\EOF && + folder1/a + EOF + test_cmp expect sparse-index-out && # Add folder1 to the sparse-checkout cone and # check that ls-files shows the expanded files. test_sparse_match git sparse-checkout add folder1 && - test_sparse_match git ls-files --modified && + test_all_match git ls-files --modified && test_all_match git ls-files && git -C sparse-index ls-files --sparse >actual && diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh index 81f3384eeed..95609046c61 100755 --- a/t/t3705-add-sparse-checkout.sh +++ b/t/t3705-add-sparse-checkout.sh @@ -19,6 +19,7 @@ setup_sparse_entry () { fi && git add sparse_entry && git update-index --skip-worktree sparse_entry && + git config core.sparseCheckout false && git commit --allow-empty -m "ensure sparse_entry exists at HEAD" && SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry) } @@ -126,6 +127,7 @@ test_expect_success 'git add --chmod does not update sparse entries' ' ' test_expect_success 'git add --renormalize does not update sparse entries' ' + test_when_finished rm .gitattributes && test_config core.autocrlf false && setup_sparse_entry "LINEONE\r\nLINETWO\r\n" && echo "sparse_entry text=auto" >.gitattributes && diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh index 7e8bf497f82..142c9aaabc5 100755 --- a/t/t6428-merge-conflicts-sparse.sh +++ b/t/t6428-merge-conflicts-sparse.sh @@ -112,7 +112,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' ' ) ' -test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' ' +test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' ' test_setup_numerals in_the_way && ( cd numerals_in_the_way && @@ -132,26 +132,13 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl test_must_fail git merge -s recursive B^0 && - git ls-files -t >index_files && - test_cmp expected-index index_files && + test_path_is_missing .git/MERGE_HEAD && - test_path_is_file README && test_path_is_file numerals && - test_cmp expected-merge numerals && - - # There should still be a file with "foobar" in it - grep foobar * && - - # 5 other files: - # * expected-merge - # * expected-index - # * index_files - # * others - # * whatever name was given to the numerals file that had - # "foobar" in it - git ls-files -o >others && - test_line_count = 5 others + # numerals should still have "foobar" in it + echo foobar >expect && + test_cmp expect numerals ) ' diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index a1080b94e38..cb9f1a6981e 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh @@ -171,50 +171,20 @@ test_expect_success 'stash restore in sparse checkout' ' # Put a file in the working directory in the way echo in the way >modified && - git stash apply && + test_must_fail git stash apply 2>error&& - # Ensure stash vivifies modifies paths... - cat >expect <<-EOF && - H addme - H modified - H removeme - H subdir/A - S untouched - EOF - git ls-files -t >actual && - test_cmp expect actual && + grep "changes.*would be overwritten by merge" error && - # ...and that the paths show up in status as changed... - cat >expect <<-EOF && - A addme - M modified - D removeme - M subdir/A - ?? actual - ?? expect - ?? modified.stash.XXXXXX - EOF - git status --porcelain | \ - sed -e s/stash......./stash.XXXXXX/ >actual && - test_cmp expect actual && + echo in the way >expect && + test_cmp expect modified && + git diff --quiet HEAD ":!modified" && # ...and that working directory reflects the files correctly - test_path_is_file addme && + test_path_is_missing addme && test_path_is_file modified && test_path_is_missing removeme && test_path_is_file subdir/A && - test_path_is_missing untouched && - - # ...including that we have the expected "modified" file... - cat >expect <<-EOF && - modified - tweaked - EOF - test_cmp expect modified && - - # ...and that the other "modified" file is still present... - echo in the way >expect && - test_cmp expect modified.stash.* + test_path_is_missing untouched ) ' diff --git a/t/t7817-grep-sparse-checkout.sh b/t/t7817-grep-sparse-checkout.sh index 590b99bbb6f..eb595645657 100755 --- a/t/t7817-grep-sparse-checkout.sh +++ b/t/t7817-grep-sparse-checkout.sh @@ -83,10 +83,13 @@ test_expect_success 'setup' ' # The test below covers a special case: the sparsity patterns exclude '/b' and # sparse checkout is enabled, but the path exists in the working tree (e.g. -# manually created after `git sparse-checkout init`). git grep should skip it. +# manually created after `git sparse-checkout init`). Although b is marked +# as SKIP_WORKTREE, git grep should notice it IS present in the worktree and +# report it. test_expect_success 'working tree grep honors sparse checkout' ' cat >expect <<-EOF && a:text + b:new-text EOF test_when_finished "rm -f b" && echo "new-text" >b && @@ -126,12 +129,16 @@ test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit' ' # Note that sub2/ is present in the worktree but it is excluded by the sparsity -# patterns, so grep should not recurse into it. +# patterns. We also explicitly mark it as SKIP_WORKTREE in case it got cleared +# by previous git commands. Thus sub2 starts as SKIP_WORKTREE but since it is +# present in the working tree, grep should recurse into it. test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' ' cat >expect <<-EOF && a:text sub/B/b:text + sub2/a:text EOF + git update-index --skip-worktree sub2 && git grep --recurse-submodules "text" >actual && test_cmp expect actual ' diff --git a/unpack-trees.c b/unpack-trees.c index 360844bda3a..96525d2ec26 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -2065,7 +2065,9 @@ static int verify_uptodate_1(const struct cache_entry *ce, int verify_uptodate(const struct cache_entry *ce, struct unpack_trees_options *o) { - if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) + if (!o->skip_sparse_checkout && + (ce->ce_flags & CE_SKIP_WORKTREE) && + (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) return 0; return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE); }