🔥 remove stash operations from pre_cycle_sync

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-12 23:07:30 -07:00
parent fd3511ed67
commit fa4d7adc98
3 changed files with 24 additions and 90 deletions

View file

@ -9,8 +9,6 @@ from .operations import (
git_push,
git_pull_rebase,
git_fetch,
git_stash,
git_stash_pop,
pre_cycle_sync,
)
from .diff_parser import summarize_diff, DiffSummary
@ -25,8 +23,6 @@ __all__ = [
"git_push",
"git_pull_rebase",
"git_fetch",
"git_stash",
"git_stash_pop",
"pre_cycle_sync",
"summarize_diff",
"DiffSummary",

View file

@ -412,25 +412,6 @@ async def git_fetch(
return False
async def git_stash(repo_path: Path) -> bool:
"""Stash uncommitted changes. Returns True if something was stashed."""
stdout, _, returncode = await _run_git_command(
"stash", cwd=repo_path, check=False
)
return returncode == 0 and "No local changes" not in stdout
async def git_stash_pop(repo_path: Path) -> bool:
"""Pop stashed changes. Returns True on success."""
_, stderr, returncode = await _run_git_command(
"stash", "pop", cwd=repo_path, check=False
)
if returncode != 0:
logger.warning(f"git stash pop failed in {repo_path}: {stderr}")
return False
return True
async def pre_cycle_sync(
repo_path: Path,
remote: str = "origin",
@ -438,16 +419,17 @@ async def pre_cycle_sync(
) -> dict:
"""Sync repo with remote before a commit cycle.
Sequence: fetch -> check if behind -> stash -> pull --rebase -> stash pop.
Sequence: fetch -> check if behind -> skip if dirty -> pull --rebase.
Never stashes dirty trees are left alone and committed normally.
All failures are non-fatal (logged and returned in result dict).
Returns dict with keys: fetched, pulled, behind_count, stashed, error
Returns dict with keys: fetched, pulled, behind_count, skipped_dirty, error
"""
result = {
"fetched": False,
"pulled": False,
"behind_count": 0,
"stashed": False,
"skipped_dirty": False,
"error": None,
}
@ -468,14 +450,18 @@ async def pre_cycle_sync(
if status.behind == 0:
return result # Nothing to pull
# Step 3: Stash if dirty
has_changes = status.has_changes
stashed = False
if has_changes:
stashed = await git_stash(repo_path)
result["stashed"] = stashed
# Step 3: Skip pull if working tree is dirty — multiple agents may be
# working concurrently, so stashing is unsafe. The dirty changes will
# get committed this cycle, pushed, and the next cycle will pull clean.
if status.has_changes:
result["skipped_dirty"] = True
logger.debug(
f"Skipping pull for {repo_path}: dirty working tree "
f"({status.behind} commits behind, will sync after commit)"
)
return result
# Step 4: Pull --rebase
# Step 4: Pull --rebase (clean tree only)
try:
await git_pull_rebase(repo_path, remote, branch)
result["pulled"] = True
@ -486,17 +472,6 @@ async def pre_cycle_sync(
logger.warning(f"Pull failed in {repo_path}: {e}")
result["error"] = f"pull_failed: {e}"
# Step 5: Unstash if we stashed
if stashed:
pop_ok = await git_stash_pop(repo_path)
if not pop_ok:
logger.warning(
f"Stash pop failed in {repo_path}. "
f"Changes are in git stash; will be picked up next cycle."
)
error_prefix = result.get("error") or ""
result["error"] = f"{error_prefix}; stash_pop_failed".lstrip("; ")
return result

View file

@ -11,8 +11,6 @@ from auto_commit_service.git import (
git_add_all,
git_commit,
git_fetch,
git_stash,
git_stash_pop,
pre_cycle_sync,
)
from auto_commit_service.git.repository import Repository
@ -160,42 +158,6 @@ class TestGitFetch:
assert result is False
class TestGitStash:
"""Tests for git_stash and git_stash_pop."""
async def test_stash_dirty_tree(self, temp_git_repo: Path) -> None:
"""Test stashing with uncommitted changes."""
(temp_git_repo / "README.md").write_text("# Modified\n")
stashed = await git_stash(temp_git_repo)
assert stashed is True
# Working tree should be clean now
status = await git_status(temp_git_repo)
assert not status.has_changes
async def test_stash_clean_tree(self, temp_git_repo: Path) -> None:
"""Test stashing with no changes returns False."""
stashed = await git_stash(temp_git_repo)
assert stashed is False
async def test_stash_pop_restores(self, temp_git_repo: Path) -> None:
"""Test stash pop restores changes."""
(temp_git_repo / "README.md").write_text("# Modified\n")
await git_stash(temp_git_repo)
popped = await git_stash_pop(temp_git_repo)
assert popped is True
status = await git_status(temp_git_repo)
assert status.has_changes
assert "README.md" in status.modified
async def test_stash_pop_empty(self, temp_git_repo: Path) -> None:
"""Test stash pop with nothing stashed returns False."""
popped = await git_stash_pop(temp_git_repo)
assert popped is False
class TestPreCycleSync:
"""Tests for pre_cycle_sync function."""
@ -237,8 +199,8 @@ class TestPreCycleSync:
# Verify the file arrived
assert (temp_git_repo / "other.txt").exists()
async def test_behind_dirty_tree(self, temp_git_repo: Path, tmp_path: Path) -> None:
"""Test sync when behind with dirty working tree — stash, pull, pop."""
async def test_behind_dirty_tree_skips_pull(self, temp_git_repo: Path, tmp_path: Path) -> None:
"""Test sync when behind with dirty working tree — skips pull entirely."""
bare = _make_bare_remote(tmp_path, temp_git_repo)
# Push a commit from another clone
@ -254,18 +216,19 @@ class TestPreCycleSync:
cwd=other, check=True, capture_output=True,
)
# Dirty the local working tree (different file to avoid conflicts)
# Dirty the local working tree
(temp_git_repo / "local_wip.txt").write_text("work in progress")
result = await pre_cycle_sync(temp_git_repo, "origin", "master")
assert result["fetched"] is True
assert result["pulled"] is True
assert result["stashed"] is True
assert result["pulled"] is False
assert result["skipped_dirty"] is True
assert result["behind_count"] == 1
assert result["error"] is None
# Remote file pulled
assert (temp_git_repo / "other.txt").exists()
# Local WIP restored from stash
# Remote file NOT pulled (skipped due to dirty tree)
assert not (temp_git_repo / "other.txt").exists()
# Local WIP untouched
assert (temp_git_repo / "local_wip.txt").exists()
async def test_fetch_failure(self, temp_git_repo: Path) -> None: