fix(gitstore): preserve staged symlinks

This commit is contained in:
chengyongru
2026-07-19 16:01:11 +08:00
committed by chengyongru
parent cea8617096
commit b1232fdaf4
2 changed files with 36 additions and 8 deletions
+2 -2
View File
@@ -168,8 +168,8 @@ 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]
"""Return absolute paths without resolving tracked-file symlinks."""
return [str((self._workspace / path).absolute()) for path in paths]
def _resolve_sha(self, short_sha: str) -> bytes | None:
"""Resolve a short SHA prefix to the full SHA bytes."""
+34 -6
View File
@@ -2,6 +2,7 @@
import subprocess
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import patch
import pytest
@@ -225,20 +226,47 @@ class TestNestedRepoProtection:
assert result is True
assert (workspace / ".git").is_dir()
def test_staging_paths_are_resolved_from_workspace(self, tmp_path, monkeypatch):
def test_staging_paths_are_absolute_from_workspace(self, tmp_path, monkeypatch):
"""Git operations should not depend on the process working directory."""
from dulwich import porcelain
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
with patch.object(porcelain, "add", wraps=porcelain.add) as mock_add:
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
(workspace / "MEMORY.md").write_text("updated\n", encoding="utf-8")
assert git.auto_commit("update memory") is not None
assert len(git.log()) == 2
assert len(mock_add.call_args_list) == 2
for call in mock_add.call_args_list:
staging_paths = [Path(path) for path in call.kwargs["paths"]]
assert all(path.is_absolute() for path in staging_paths)
assert all(path.is_relative_to(workspace) for path in staging_paths)
def test_staging_paths_preserve_symlinks(self, tmp_path):
"""Absolute staging paths should still identify the tracked symlink itself."""
workspace = tmp_path / "workspace"
workspace.mkdir()
target = tmp_path / "shared-memory.md"
target.write_text("shared\n", encoding="utf-8")
link = workspace / "MEMORY.md"
try:
link.symlink_to(target)
except OSError as exc:
pytest.skip(f"symlinks unavailable: {exc}")
git = GitStore(workspace, tracked_files=["MEMORY.md"])
staging_path = Path(git._staging_paths("MEMORY.md")[0])
assert staging_path == link.absolute()
assert staging_path.is_symlink()
def test_init_refuses_inside_git_worktree(self, tmp_path):
"""init() should refuse when the parent checkout is a git worktree."""