git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 2/2] unpack-trees: use traverse_path instead of name

From
Elijah Newren <newren@gmail.com>
Date
Dec 4, 2021, 05:42 UTC
Message-ID
<CABPp-BEs8_F7SKsxDMd8p03oMn9M3H47=hrPP=AzxsacUkGg_A@mail.gmail.com>
In-Reply-To
<c91421996562ecedab18f8af4ed8a02896584540.1638586534.git.gitgitgadget@gmail.com>

On Fri, Dec 3, 2021 at 6:55 PM Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com> wrote:

Show 51 quoted lines
>
> From: Derrick Stolee <dstolee@microsoft.com>
>
> The sparse_dir_matches_path() method compares a cache entry that is a
> sparse directory entry against a 'struct traverse_info *info' and a
> 'struct name_entry *p' to see if the cache entry has exactly the right
> name for those other inputs.
>
> This method was introduced in 523506d (unpack-trees: unpack sparse
> directory entries, 2021-07-14), but included a significant mistake. The
> path comparisons used 'info->name' instead of 'info->traverse_path'.
> Since 'info->name' only stores a single tree entry name while
> 'info->traverse_path' stores the full path from root, this method does
> not work when 'info' is in a subdirectory of a directory. Replacing the
> right strings and their corresponding lengths make the method work
> properly.
>
> The previous change included a failing test that exposes this issue.
> That test now passes. The critical detail is that as we go deep into
> unpack_trees(), the logic for merging a sparse directory entry with a
> tree entry during 'git checkout' relies on this
> sparse_dir_matches_path() in order to avoid calling
> traverse_trees_recursive() during unpack_callback() in this hunk:
>
>         if (!is_sparse_directory_entry(src[0], names, info) &&
>             traverse_trees_recursive(n, dirmask, mask & ~dirmask,
>                                             names, info) < 0) {
>                 return -1;
>         }
>
> For deep paths, the short-circuit never occurred and
> traverse_trees_recursive() was being called incorrectly and that was
> causing other strange issues. Specifically, the error message from the
> now-passing test previously included this:
>
>       error: Your local changes to the following files would be overwritten by checkout:
>               deep/deeper1/deepest2/a
>               deep/deeper1/deepest3/a
>       Please commit your changes or stash them before you switch branches.
>       Aborting
>
> These messages occurred because the 'current' cache entry in
> twoway_merge() was showing as NULL because the index did not contain
> entries for the paths contained within the sparse directory entries. We
> instead had 'oldtree' given as the entry at HEAD and 'newtree' as the
> entry in the target tree. This led to reject_merge() listing these
> paths.
>
> Now that sparse_dir_matches_path() works the same for deep paths as it
> does for shallow depths, the rest of the logic kicks in to properly
> handle modifying the sparse directory entries as designed.

Eek, sorry for not catching this in my earlier review. Thanks for the detailed explanation; well analyzed.

Show 44 quoted lines
>
> Reported-by: Gustave Granroth <gus.gran@gmail.com>
> Reported-by: Mike Marcelais <michmarc@exchange.microsoft.com>
> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
> ---
>  t/t1092-sparse-checkout-compatibility.sh |  2 +-
>  unpack-trees.c                           | 10 +++++-----
>  2 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
> index e6aef40e9b3..f04a02c6b20 100755
> --- a/t/t1092-sparse-checkout-compatibility.sh
> +++ b/t/t1092-sparse-checkout-compatibility.sh
> @@ -307,7 +307,7 @@ test_expect_success 'add, commit, checkout' '
>         test_all_match git checkout -
>  '
>
> -test_expect_failure 'deep changes during checkout' '
> +test_expect_success 'deep changes during checkout' '
>         init_repos &&
>
>         test_sparse_match git sparse-checkout set deep/deeper1/deepest &&
> diff --git a/unpack-trees.c b/unpack-trees.c
> index 89ca95ce90b..7381c275768 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -1243,11 +1243,11 @@ static int sparse_dir_matches_path(const struct cache_entry *ce,
>         assert(S_ISSPARSEDIR(ce->ce_mode));
>         assert(ce->name[ce->ce_namelen - 1] == '/');
>
> -       if (info->namelen)
> -               return ce->ce_namelen == info->namelen + p->pathlen + 2 &&
> -                      ce->name[info->namelen] == '/' &&
> -                      !strncmp(ce->name, info->name, info->namelen) &&
> -                      !strncmp(ce->name + info->namelen + 1, p->path, p->pathlen);
> +       if (info->pathlen)
> +               return ce->ce_namelen == info->pathlen + p->pathlen + 1 &&
> +                      ce->name[info->pathlen - 1] == '/' &&
> +                      !strncmp(ce->name, info->traverse_path, info->pathlen) &&
> +                      !strncmp(ce->name + info->pathlen, p->path, p->pathlen);
>         return ce->ce_namelen == p->pathlen + 1 &&
>                !strncmp(ce->name, p->path, p->pathlen);
>  }
> --

The comment at the beginning of this function (not shown in this patch) is now stale and misleading; it should be corrected too.

Previous: Derrick Stolee via GitGitGadgetNext: Derrick Stolee
Message 3 of 9 in “Sparse Index: fix a checkout bug with deep sparse-checkout patterns”
  1. 0/2 Sparse Index: fix a checkout bug with deep sparse-checkout patternsDerrick Stolee via GitGitGadget, Dec 4, 2021
  2. 2/2 unpack-trees: use traverse_path instead of nameDerrick Stolee via GitGitGadget, Dec 4, 2021
  3. Elijah NewrenDec 4, 2021
  4. Derrick StoleeDec 6, 2021
  5. 1/2 t1092: add deeper changes during a checkoutDerrick Stolee via GitGitGadget, Dec 4, 2021
  6. Elijah NewrenDec 4, 2021
  7. 0/2 Sparse Index: fix a checkout bug with deep sparse-checkout patternsDerrick Stolee via GitGitGadget, Dec 6, 2021
  8. 1/2 t1092: add deeper changes during a checkoutDerrick Stolee via GitGitGadget, Dec 6, 2021
  9. 2/2 unpack-trees: use traverse_path instead of nameDerrick Stolee via GitGitGadget, Dec 6, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.