From cea86170966a4c5792a89d9c5be30d419267453c Mon Sep 17 00:00:00 2001 From: Pei Futong <1262280216@qq.com> Date: Sun, 19 Jul 2026 03:25:58 +0800 Subject: [PATCH] fix(gitstore): resolve staged paths relative to workspace --- nanobot/utils/gitstore.py | 11 +++++++++-- tests/utils/test_gitstore.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/nanobot/utils/gitstore.py b/nanobot/utils/gitstore.py index 4d40e2cd..338bbcfb 100644 --- a/nanobot/utils/gitstore.py +++ b/nanobot/utils/gitstore.py @@ -113,7 +113,10 @@ class GitStore: p.write_text("", encoding="utf-8") # Initial commit - porcelain.add(str(self._workspace), paths=[".gitignore"] + self._tracked_files) + porcelain.add( + str(self._workspace), + paths=self._staging_paths(".gitignore", *self._tracked_files), + ) porcelain.commit( str(self._workspace), message=b"init: nanobot memory store", @@ -146,7 +149,7 @@ class GitStore: return None msg_bytes = message.encode("utf-8") if isinstance(message, str) else message - porcelain.add(str(self._workspace), paths=self._tracked_files) + porcelain.add(str(self._workspace), paths=self._staging_paths(*self._tracked_files)) sha_bytes = porcelain.commit( str(self._workspace), message=msg_bytes, @@ -164,6 +167,10 @@ class GitStore: # -- internal helpers ------------------------------------------------------ + def _staging_paths(self, *paths: str) -> list[str]: + """Return absolute paths so Dulwich resolves them inside this workspace.""" + return [str((self._workspace / path).resolve()) for path in paths] + def _resolve_sha(self, short_sha: str) -> bytes | None: """Resolve a short SHA prefix to the full SHA bytes.""" try: diff --git a/tests/utils/test_gitstore.py b/tests/utils/test_gitstore.py index 331e2078..dda5e239 100644 --- a/tests/utils/test_gitstore.py +++ b/tests/utils/test_gitstore.py @@ -225,6 +225,21 @@ class TestNestedRepoProtection: assert result is True assert (workspace / ".git").is_dir() + def test_staging_paths_are_resolved_from_workspace(self, tmp_path, monkeypatch): + """Git operations should not depend on the process working directory.""" + workspace = tmp_path / "workspace" + workspace.mkdir() + monkeypatch.chdir(tmp_path) + + git = GitStore(workspace, tracked_files=["MEMORY.md"]) + + assert git.init() is True + assert len(git.log()) == 1 + + (workspace / "MEMORY.md").write_text("updated\n", encoding="utf-8") + assert git.auto_commit("update memory") is not None + assert len(git.log()) == 2 + def test_init_refuses_inside_git_worktree(self, tmp_path): """init() should refuse when the parent checkout is a git worktree.""" repo = tmp_path / "repo"