From 772eabd2ff9784c2219bd18650313db1d4876697 Mon Sep 17 00:00:00 2001 From: Faris Hamdi Date: Wed, 18 Mar 2026 13:50:14 -0400 Subject: [PATCH 1/2] Re-apply sparse-checkout paths on subsequent installs --- gitman/git.py | 12 +++++++++++- gitman/models/source.py | 4 ++++ tests/test_api.py | 42 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/gitman/git.py b/gitman/git.py index fe9f26c1..062fa4c9 100644 --- a/gitman/git.py +++ b/gitman/git.py @@ -12,6 +12,11 @@ from .shell import call, pwd +def sanitize_sparse_paths(sparse_paths): + """Strip trailing glob patterns for cone mode (e.g. 'src/*' -> 'src').""" + return [p.rstrip("/*") if p.endswith("/*") else p for p in sparse_paths] + + def git(*args, **kwargs): return call("git", *args, **kwargs) @@ -69,7 +74,7 @@ def clone( fd.write("%s/objects" % sparse_paths_repo) git("-C", normpath, "sparse-checkout", "init", "--cone") - git("-C", normpath, "sparse-checkout", "set", *sparse_paths) + git("-C", normpath, "sparse-checkout", "set", *sanitize_sparse_paths(sparse_paths)) git("-C", normpath, "fetch", "origin") git("-C", normpath, "checkout", rev) elif settings.CACHE_DISABLE: @@ -245,6 +250,11 @@ def am(patch, _skip=False): raise ShellError from e +def apply_sparse_checkout(sparse_paths): + """Re-apply sparse-checkout paths to an existing working tree.""" + git("sparse-checkout", "set", *_sanitize_sparse_paths(sparse_paths)) + + def update( type, repo, path, *, clean=True, fetch=False, rev=None ): # pylint: disable=redefined-outer-name,unused-argument diff --git a/gitman/models/source.py b/gitman/models/source.py index 29471506..3376b181 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -204,6 +204,10 @@ def update_files( if fetch or git.is_fetch_required(self.type, self.rev): git.fetch(self.type, self.repo, self.name, rev=self.rev) + # Re-apply sparse-checkout paths in case they changed since initial clone + if self.sparse_paths and self.sparse_paths[0]: + git.apply_sparse_checkout(self.sparse_paths) + # Update the working tree to the desired revision git.update( self.type, self.repo, self.name, fetch=fetch, clean=clean, rev=self.rev diff --git a/tests/test_api.py b/tests/test_api.py index 207028f0..38ff8b87 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -391,6 +391,48 @@ def it_contains_only_the_sparse_paths(config): expect(dir_listing).contains("src") expect(len(dir_listing) == 1) + def it_updates_sparse_paths_on_subsequent_install(config): + config.datafile.text = strip(""" + location: deps + sources: + - name: gitman_1 + type: git + params: + repo: https://github.com/jacebrowning/gitman-demo + sparse_paths: + - src/* + rev: ddbe17ef173538d1fda29bd99a14bab3c5d86e78 + links: + - + scripts: + - + """) + config.datafile.load() + expect(gitman.install(depth=1, force=True)) == True + dir_listing = os.listdir(os.path.join(config.location, "gitman_1")) + expect(dir_listing).contains("src") + + # Change sparse_paths and re-install to verify update takes effect + config.datafile.text = strip(""" + location: deps + sources: + - name: gitman_1 + type: git + params: + repo: https://github.com/jacebrowning/gitman-demo + sparse_paths: + - nonexistent_dir/* + rev: ddbe17ef173538d1fda29bd99a14bab3c5d86e78 + links: + - + scripts: + - + """) + config.datafile.load() + expect(gitman.install(depth=1, force=True)) == True + dir_listing = os.listdir(os.path.join(config.location, "gitman_1")) + expect("src" not in dir_listing) + def describe_mixed_names(): @pytest.fixture def config_with_group(config): From 0e3f8d0b9647212148491954eaa3061fff546dea Mon Sep 17 00:00:00 2001 From: Faris Hamdi Date: Wed, 18 Mar 2026 17:17:55 -0400 Subject: [PATCH 2/2] linter fixes and function call correction --- gitman/git.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gitman/git.py b/gitman/git.py index 062fa4c9..46413080 100644 --- a/gitman/git.py +++ b/gitman/git.py @@ -74,7 +74,13 @@ def clone( fd.write("%s/objects" % sparse_paths_repo) git("-C", normpath, "sparse-checkout", "init", "--cone") - git("-C", normpath, "sparse-checkout", "set", *sanitize_sparse_paths(sparse_paths)) + git( + "-C", + normpath, + "sparse-checkout", + "set", + *sanitize_sparse_paths(sparse_paths) + ) git("-C", normpath, "fetch", "origin") git("-C", normpath, "checkout", rev) elif settings.CACHE_DISABLE: @@ -252,7 +258,7 @@ def am(patch, _skip=False): def apply_sparse_checkout(sparse_paths): """Re-apply sparse-checkout paths to an existing working tree.""" - git("sparse-checkout", "set", *_sanitize_sparse_paths(sparse_paths)) + git("sparse-checkout", "set", *sanitize_sparse_paths(sparse_paths)) def update(