Multi-milestone work and documents on the new loop (M2.4)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 3m45s
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 3m45s
- Every milestone branch is cut from the latest default branch: the next milestone builds on the merged previous one, and a redo after a closed PR drops the rejected work. Uncommitted work comes along. - While the item's PR is open, start_milestone is refused, with the PR's state read from Gitea at that moment, not the last watch round. - finish_milestone(final): earlier PRs say "Part of #N", the last "Closes #N"; unsaid, it's final, as before. - close_item with the item's PR still open waits for the merge (L2: a closed item leaves nothing open). - The driver's instructions cover ARCHITECTURE.md and build plans under docs/plans/ (D2); the harness never parses a plan. - Parking stashes uncommitted work and pushes it to <item>-parked, put back on resume, instead of committing it onto the item's branch. The walk found a half-done review fix parked onto a PR that had merged meanwhile, where it was lost. Architecture D4's parking line says so. Tests: unit tests for fresh milestone branches, the merge wait (including a merge not yet watched), redo after a closed PR, Part of/Closes, close waiting on an open PR, and the parked-fix regression; the walk's new milestones_start_fresh property (fork-from-current fails 9 seeds); an e2e two-milestone feature. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
da32cac5cf
commit
cd9ab2559e
10 files changed
+539
-53
No files matched your search
@@ -210,8 +210,10 @@ class GitRepo:
|
||||
self._git("config", "user.name", name)
|
||||
self._git("config", "user.email", email)
|
||||
|
||||
def checkout_new_branch(self, name: str) -> None:
|
||||
self._git("checkout", "-q", "-b", name)
|
||||
def checkout_new_branch(self, name: str, start: str | None = None) -> None:
|
||||
"""A new branch, at ``start`` if given. Uncommitted changes come
|
||||
along; git refuses if one would conflict with ``start``."""
|
||||
self._git("checkout", "-q", "-b", name, *([start] if start else []))
|
||||
|
||||
def checkout_existing_branch(self, name: str) -> None:
|
||||
self._git("checkout", "-q", name)
|
||||
@@ -300,6 +302,46 @@ class GitRepo:
|
||||
"push", "-q", remote_url, f"HEAD:refs/heads/{branch}", credential=credential
|
||||
)
|
||||
|
||||
def push_commit(
|
||||
self,
|
||||
remote_url: str,
|
||||
sha: str,
|
||||
branch: str,
|
||||
*,
|
||||
credential: PushCredential | None = None,
|
||||
) -> None:
|
||||
"""Point the remote's ``branch`` at ``sha``, whatever it was."""
|
||||
self._git(
|
||||
"push",
|
||||
"-q",
|
||||
"-f",
|
||||
remote_url,
|
||||
f"{sha}:refs/heads/{branch}",
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
def stash_all(self, message: str) -> str | None:
|
||||
"""Take every uncommitted change, untracked files included and build
|
||||
artifacts not, off the work tree as a stash commit; returns its sha,
|
||||
or None when there was nothing. The commit is kept by pushing it."""
|
||||
if not self.has_changes():
|
||||
return None
|
||||
self._git("stash", "push", "-q", "-u", "-m", message)
|
||||
sha = self._git("rev-parse", "stash@{0}")
|
||||
self._git("stash", "drop", "-q")
|
||||
return sha
|
||||
|
||||
def apply_stash(self, sha: str) -> None:
|
||||
"""Put a stash commit's changes back on the work tree, uncommitted."""
|
||||
self._git("stash", "apply", "-q", sha)
|
||||
|
||||
def has_commit(self, sha: str) -> bool:
|
||||
try:
|
||||
self._git("cat-file", "-e", f"{sha}^{{commit}}")
|
||||
except GitError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def discard_branch(self, branch: str, *, back_to: str) -> None:
|
||||
"""Throw away a branch and everything on it.
|
||||
|
||||
|
||||
Reference in new issue
Block a user