Skip to content

Commit 5575546

Browse files
committed
sync/git(feat[GitSync]): Honor arbitrary clone depth
why: GitSync could express shallow-vs-full but not a numeric clone depth, so downstream tools could only persist a boolean shallow flag. Addresses the core of #531. what: - Add a depth keyword-only param to GitSync; obtain() forwards it to git clone --depth N. An explicit depth wins over git_shallow; unset keeps the prior depth-1-if-git_shallow-else-full behavior - Document depth in the GitSync docstring - Record the deferred update-time deepen/unshallow (#532) in a update_repo() .. todo:: with the two git edges it must handle - Parametrized obtain() depth matrix test (full / git_shallow / depth=N / depth-overrides-git_shallow) over a 6-commit file:// remote - CHANGES: What's new entry
1 parent 87953b6 commit 5575546

3 files changed

Lines changed: 107 additions & 2 deletions

File tree

CHANGES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ $ uv add libvcs --prerelease allow
2020
_Notes on the upcoming release will go here._
2121
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
2222

23+
### What's new
24+
25+
#### GitSync honors an arbitrary clone depth (#531)
26+
27+
{class}`~libvcs.sync.git.GitSync` accepts a `depth` keyword argument that {meth}`~libvcs.sync.git.GitSync.obtain` forwards to `git clone --depth N`. When `depth` is unset the prior behavior is preserved: `git_shallow=True` clones at depth 1, and otherwise the clone is full. Downstream tools can now persist and apply a numeric shallow depth instead of only a boolean shallow flag.
28+
2329
### Fixes
2430

2531
#### GitSync honors `git_shallow` and `tls_verify` constructor arguments (#531)

src/libvcs/sync/git.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ def __init__(
213213
remotes: GitRemotesArgs = None,
214214
git_shallow: bool = False,
215215
tls_verify: bool = False,
216+
depth: int | None = None,
216217
**kwargs: t.Any,
217218
) -> None:
218219
"""Local git repository.
@@ -226,6 +227,11 @@ def __init__(
226227
Clone with history truncated to the latest commit (``--depth 1``,
227228
default False)
228229
230+
depth : int, optional
231+
Clone with history truncated to ``depth`` commits
232+
(``git clone --depth N``). Takes precedence over ``git_shallow``.
233+
Default None (full clone).
234+
229235
tls_verify : bool
230236
Should certificate for https be checked (default False)
231237
@@ -266,6 +272,7 @@ def __init__(
266272
"""
267273
self.git_shallow = git_shallow
268274
self.tls_verify = tls_verify
275+
self.depth = depth
269276

270277
self._remotes: GitSyncRemoteDict
271278

@@ -368,10 +375,19 @@ def obtain(self, *args: t.Any, **kwargs: t.Any) -> None:
368375
url = self.url
369376

370377
self.log.info("Cloning.")
378+
# An explicit depth wins; otherwise git_shallow keeps the depth-1
379+
# behavior, and neither means a full clone.
380+
clone_depth: int | None
381+
if self.depth is not None:
382+
clone_depth = self.depth
383+
elif self.git_shallow:
384+
clone_depth = 1
385+
else:
386+
clone_depth = None
371387
self.cmd.clone(
372388
url=url,
373389
progress=True,
374-
depth=1 if self.git_shallow else None,
390+
depth=clone_depth,
375391
config={"http.sslVerify": False} if self.tls_verify else None,
376392
log_in_real_time=True,
377393
)
@@ -396,6 +412,15 @@ def update_repo(
396412
) -> SyncResult:
397413
"""Pull latest changes from git remote.
398414
415+
.. todo::
416+
417+
Honor ``depth`` on update by deepening or unshallowing the existing
418+
checkout when the requested depth differs from what is on disk.
419+
Tracked in https://github.com/vcs-python/libvcs/issues/532. Edges to
420+
handle: ``git fetch --depth N`` against a full checkout truncates it
421+
to shallow, and ``git fetch --unshallow`` against a complete repo is
422+
a fatal error (guard with ``git rev-parse --is-shallow-repository``).
423+
399424
Parameters
400425
----------
401426
set_remotes : bool

tests/sync/test_git.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import datetime
6+
import os
67
import pathlib
78
import random
89
import shutil
@@ -28,7 +29,7 @@
2829
if t.TYPE_CHECKING:
2930
from pytest_mock import MockerFixture
3031

31-
from libvcs.pytest_plugin import CreateRepoFn
32+
from libvcs.pytest_plugin import CreateRepoFn, GitCommitEnvVars
3233

3334
if not shutil.which("git"):
3435
pytestmark = pytest.mark.skip(reason="git is not available")
@@ -157,6 +158,79 @@ def test_git_shallow_and_tls_verify_kwargs_are_honored(
157158
assert is_shallow == "true"
158159

159160

161+
class DepthFixture(t.NamedTuple):
162+
"""Parameters for :func:`test_obtain_honors_clone_depth`."""
163+
164+
test_id: str
165+
sync_kwargs: dict[str, t.Any]
166+
expected_count: int
167+
expected_shallow: bool
168+
169+
170+
DEPTH_FIXTURES: list[DepthFixture] = [
171+
DepthFixture(
172+
test_id="full-clone",
173+
sync_kwargs={},
174+
expected_count=6,
175+
expected_shallow=False,
176+
),
177+
DepthFixture(
178+
test_id="git_shallow-depth-1",
179+
sync_kwargs={"git_shallow": True},
180+
expected_count=1,
181+
expected_shallow=True,
182+
),
183+
DepthFixture(
184+
test_id="depth-3",
185+
sync_kwargs={"depth": 3},
186+
expected_count=3,
187+
expected_shallow=True,
188+
),
189+
DepthFixture(
190+
test_id="depth-overrides-git_shallow",
191+
sync_kwargs={"git_shallow": True, "depth": 2},
192+
expected_count=2,
193+
expected_shallow=True,
194+
),
195+
]
196+
197+
198+
@pytest.mark.parametrize(
199+
list(DepthFixture._fields),
200+
DEPTH_FIXTURES,
201+
ids=[test.test_id for test in DEPTH_FIXTURES],
202+
)
203+
def test_obtain_honors_clone_depth(
204+
tmp_path: pathlib.Path,
205+
create_git_remote_repo: CreateRepoFn,
206+
git_commit_envvars: GitCommitEnvVars,
207+
test_id: str,
208+
sync_kwargs: dict[str, t.Any],
209+
expected_count: int,
210+
expected_shallow: bool,
211+
) -> None:
212+
"""obtain() clones at the requested depth; an explicit depth wins.
213+
214+
The ``file://`` URL matters: git ignores ``--depth`` for local-path clones.
215+
"""
216+
remote_repo = create_git_remote_repo()
217+
env = os.environ.copy()
218+
env.update(git_commit_envvars)
219+
for i in range(1, 7):
220+
(remote_repo / "f.txt").write_text(str(i))
221+
run(["git", "add", "f.txt"], cwd=remote_repo, env=env)
222+
run(["git", "commit", "-m", f"c{i}"], cwd=remote_repo, env=env)
223+
224+
checkout = tmp_path / "checkout"
225+
git_repo = GitSync(url=remote_repo.as_uri(), path=checkout, **sync_kwargs)
226+
git_repo.obtain()
227+
228+
commit_count = run(["git", "rev-list", "--count", "HEAD"], cwd=checkout)
229+
is_shallow = run(["git", "rev-parse", "--is-shallow-repository"], cwd=checkout)
230+
assert int(commit_count) == expected_count
231+
assert is_shallow == ("true" if expected_shallow else "false")
232+
233+
160234
@pytest.mark.parametrize(
161235
# Postpone evaluation of options so fixture variables can interpolate
162236
("constructor", "lazy_constructor_options"),

0 commit comments

Comments
 (0)