From d32573ec6ddf1a9e14ce10c939d6dfada2997cc9 Mon Sep 17 00:00:00 2001 From: Ethan Meng Date: Wed, 29 Apr 2026 22:49:34 -0500 Subject: [PATCH 1/3] fix(repository): dereference reflog identity correctly Since its introduction in commit 81520c9c (Update to libgit2 v0.23), the reflog identity getter (Repository.ident) has been passing the (char **) variables directly to ffi.string() as C-strings, when they really are pointers to C-strings. Fix by dereferencing the pointer to a C-string first. Furthermore, Repository.ident assumes that said variables hold valid C-strings, when the reflog identity is initialized to NULL, and can be explicitly set to such with `set_ident (None, None)`. So map None to ffi.NULL (which maybe_string() happens to already do, so I simply delegated to that...). I also added a few regression tests to ensure neither issue shows up again. Signed-off-by: Ethan Meng --- pygit2/repository.py | 4 ++-- test/test_repository.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index b5e59e87..b03342f8 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -81,7 +81,7 @@ from .remotes import RemoteCollection from .submodules import SubmoduleCollection from .transaction import ReferenceTransaction -from .utils import StrArray, to_bytes +from .utils import StrArray, maybe_string, to_bytes if TYPE_CHECKING: from pygit2._libgit2.ffi import ( @@ -1585,7 +1585,7 @@ def ident(self): err = C.git_repository_ident(cname, cemail, self._repo) check_error(err) - return (ffi.string(cname).decode('utf-8'), ffi.string(cemail).decode('utf-8')) + return (maybe_string(cname[0]), maybe_string(cemail[0])) def set_ident(self, name: str, email: str) -> None: """Set the identity to be used for reference operations. diff --git a/test/test_repository.py b/test/test_repository.py index 68913ff5..446aaae7 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -641,6 +641,19 @@ def test_default_signature(testrepo: Repository) -> None: assert 'rjh@example.com' == sig.email +def test_ident_get_set(testrepo: Repository) -> None: + # By default, reflog identity should be unset. + assert testrepo.ident == (None, None) + + cname = 'C O Mitter' + cemail = 'committer@example.com' + testrepo.set_ident(cname, cemail) + assert testrepo.ident == (cname, cemail) + + testrepo.set_ident(None, None) + assert testrepo.ident == (None, None) + + def test_new_repo(tmp_path: Path) -> None: repo = init_repository(tmp_path, False) From 379b4d82f0d8d97a9f06cdb37286a735dd01244e Mon Sep 17 00:00:00 2001 From: Ethan Meng Date: Thu, 30 Apr 2026 00:19:03 -0500 Subject: [PATCH 2/3] fix(repository): amend annotations on reflog identity Since set_ident() already has type annotations, it seems fitting that we adorn ident() with those too. :) Also, to_bytes() is perfectly capable of mapping None to ffi.NULL, so the parameters on set_ident() should be `str | None` (aka Optional[str]). Signed-off-by: Ethan Meng --- pygit2/repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index b03342f8..506fc125 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -1578,7 +1578,7 @@ def get_attr( # Identity for reference operations # @property - def ident(self): + def ident(self) -> tuple[Optional[str], Optional[str]]: cname = ffi.new('char **') cemail = ffi.new('char **') @@ -1587,7 +1587,7 @@ def ident(self): return (maybe_string(cname[0]), maybe_string(cemail[0])) - def set_ident(self, name: str, email: str) -> None: + def set_ident(self, name: Optional[str], email: Optional[str]) -> None: """Set the identity to be used for reference operations. Updates to some references also append data to their From e7bd6d26c6d7ffd17bfb1e5c52d39a76d95fb1c7 Mon Sep 17 00:00:00 2001 From: Ethan Meng Date: Wed, 29 Apr 2026 23:20:58 -0500 Subject: [PATCH 3/3] docs(repository): fix default_signature typos Repository.default_signature is a read-only descriptor, not a getter method. Signed-off-by: Ethan Meng --- pygit2/repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index 506fc125..660ac4fa 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -1278,7 +1278,7 @@ def stash( Example:: >>> repo = pygit2.Repository('.') - >>> repo.stash(repo.default_signature(), 'WIP: stashing') + >>> repo.stash(repo.default_signature, 'WIP: stashing') """ opts = ffi.new('git_stash_save_options *') @@ -1347,7 +1347,7 @@ def stash_apply( Example:: >>> repo = pygit2.Repository('.') - >>> repo.stash(repo.default_signature(), 'WIP: stashing') + >>> repo.stash(repo.default_signature, 'WIP: stashing') >>> repo.stash_apply(strategy=CheckoutStrategy.ALLOW_CONFLICTS) """ with git_stash_apply_options(