Skip to content

Commit 837a4f0

Browse files
Add Commit.is_shallow property and document stats() limitation at shallow boundary
Accessing .stats on a commit at the boundary of a shallow clone raises GitCommandError because the commit's parent SHA was never fetched. This adds an is_shallow property to detect this case ahead of time by checking the repository's shallow file, and documents the limitation on stats(). Co-authored-by: Claude <noreply@anthropic.com>
1 parent b920dcd commit 837a4f0

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,6 @@ Contributors are:
5757
-Jonas Scharpf <jonas.scharpf _at_ checkmk.com>
5858
-Gordon Marx
5959
-Enji Cooper
60+
-Harshita Yadav <harshitayadav504 _at_ gmail.com>
6061

6162
Portions derived from other open source works and are clearly marked.

git/objects/commit.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,12 +368,38 @@ def iter_parents(self, paths: Union[PathLike, Sequence[PathLike]] = "", **kwargs
368368
kwargs["skip"] = skip
369369

370370
return self.iter_items(self.repo, self, paths, **kwargs)
371+
@property
372+
def is_shallow(self) -> bool:
373+
"""Check whether this commit is a shallow boundary (graft) commit.
374+
375+
A commit at the boundary of a shallow clone appears to have no
376+
parents from Git's perspective, even though the underlying commit
377+
object still references a parent SHA that was never fetched. Calling
378+
:attr:`stats` (or anything else that diffs against the parent) on
379+
such a commit will raise :exc:`~git.exc.GitCommandError` because the
380+
parent object does not exist locally.
371381
382+
:return:
383+
True if this commit's hexsha appears in the repository's
384+
``shallow`` file, False otherwise (including for non-shallow
385+
repositories).
386+
"""
387+
shallow_file = os.path.join(self.repo.git_dir, "shallow")
388+
if not os.path.isfile(shallow_file):
389+
return False
390+
with open(shallow_file, "r") as f:
391+
return self.hexsha in f.read().split()
392+
372393
@property
373394
def stats(self) -> Stats:
374395
"""Create a git stat from changes between this commit and its first parent
375396
or from all changes done if this is the very first commit.
376397
398+
:note:
399+
If this commit is at the boundary of a shallow clone (see
400+
:attr:`is_shallow`), this will raise :exc:`~git.exc.GitCommandError`
401+
because the parent object was never fetched.
402+
377403
:return:
378404
:class:`Stats`
379405
"""

test/test_commit.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from gitdb import IStream
1616

17-
from git import Actor, Commit, Repo
17+
from git import Actor, Commit, GitCommandError, Repo
1818
from git.objects.util import tzoffset, utc
1919
from git.repo.fun import touch
2020

@@ -163,6 +163,25 @@ def check_entries(d, has_change_type=False):
163163
self.assertEqual(commit.author_tz_offset, 14400, commit.author_tz_offset)
164164
self.assertEqual(commit.committer_tz_offset, 14400, commit.committer_tz_offset)
165165
self.assertEqual(commit.message, "initial project\n")
166+
167+
@with_rw_directory
168+
def test_is_shallow(self, rw_dir):
169+
"""A commit at the shallow boundary should report is_shallow, and
170+
accessing its stats should raise GitCommandError."""
171+
full_repo = self.rorepo
172+
shallow_path = osp.join(rw_dir, "shallow_clone")
173+
shallow_repo = Repo.clone_from(full_repo.git_dir, shallow_path, depth=2, no_local=True)
174+
175+
commits = list(shallow_repo.iter_commits())
176+
boundary_commit = commits[-1]
177+
178+
self.assertTrue(boundary_commit.is_shallow)
179+
with self.assertRaises(GitCommandError):
180+
boundary_commit.stats
181+
182+
if len(commits) > 1:
183+
non_boundary_commit = commits[0]
184+
self.assertFalse(non_boundary_commit.is_shallow)
166185

167186
def test_renames(self):
168187
commit = self.rorepo.commit("185d847ec7647fd2642a82d9205fb3d07ea71715")

0 commit comments

Comments
 (0)