Skip to content

Push past target-repo hooks, show git's stdout on failure, and keep unpushed work - #56

Merged
JacobStephens2 merged 4 commits into
mainfrom
issue-55
Sep 26, 2026
Merged

JacobStephens2 merged 4 commits into
mainfrom
issue-55

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

A git hook in the target repo can no longer block a Run's pushes. Failed git commands now say why they failed. A Failed run whose work didn't reach origin keeps that work locally.

 Worktree::push
-  git push origin <Issue branch>
+  git push --no-verify origin <Issue branch>      # used by the Failed run path too

 Git::run, on a non-zero exit
-  "git … failed: <stderr>"
+  "git … failed: <last 10 lines of stderr>\n<last 10 lines of stdout>"   # nothing extra for an empty stream

 failed_run::fail(issue, worktree, …)                # now takes the Worktree by value
   pushed = commit_and_push()
   convert the open PR to a draft
+  if pushed failed
+    worktree.keep()
   drop → remove the worktree and local Issue branch
+       (if kept: print "keeping the worktree <path> and local branch <branch> at <sha>" instead)

README, CONTEXT.md and ADR 0002 now describe the hook skipping and the kept worktree. The integration tests share a launch_has_rejecting_pre_push_hook scenario helper.

Evidence

  • Before: the new tests fail against main's code:

    • Git::run pushes to a bare repo whose pre-push hook echoes hook says no and exits 1. The error says only failed to push some refs; the hook's line is missing. (On git 2.43 too, the hook's output went to stdout, so this test really does exercise the stdout path.)
    • failed_run: when an origin pre-receive hook rejects the push, nothing says what was kept, and the worktree and branch are deleted.
    • failed_run: when a pre-push hook in the launch clone exits 1, the failure commit never reaches origin.

    After: those tests and the new ones below all pass. cargo clippy --all-targets and cargo fmt --check are clean.

    • fresh_run::a_rejecting_pre_push_hook_in_the_target_repo_does_not_block_the_push
    • failed_run::a_rejecting_pre_push_hook_in_the_target_repo_does_not_block_the_failure_push
    • failed_run::when_origin_rejects_the_failure_push_the_worktree_and_local_branch_are_kept
    • failed_run::when_origin_is_unreachable_for_the_failure_push_the_worktree_and_local_branch_are_kept
    • git::tests::a_failure_includes_a_hooks_output_and_gits_own_error
    • git::tests::a_failure_includes_stdout
    • git::tests::a_failure_keeps_only_the_last_lines_of_a_stream
    • git::tests::a_failure_with_nothing_on_either_stream_says_only_that_git_failed
  • The existing cleanup tests still pass, including the case where there's nothing to push.

  • cargo test --no-fail-fast passes except ci::waits_for_github_to_work_out_whether_the_pr_is_mergeable. That test fails intermittently under parallel load, and it fails the same way on an untouched main checkout.

Merge Danger

Door: two-way

Reverting brings back the old push, error and cleanup behavior.

Blast Radius: pushes

Every Run now skips the target repo's local git hooks when it pushes. That's intended: the session runs the tests and CI gates the PR. But a team that relied on a local pre-push hook as a gate loses it for thirdshift's pushes. Git failure messages get longer, up to 20 lines. A Failed run whose push fails now leaves a worktree next to the clone, which you have to remove by hand.

Unaddressed findings

Standards

  • Duplicated isolated-git and hook-install helpers between the src/git.rs unit tests and tests/support. Unit tests and integration tests are separate crates. Sharing about 10 lines would need a new test-utility module.
  • kept: bool flag switch in Drop for Worktree. The reviewer judged it acceptable at this size.
  • pushed.is_err() checked twice in failed_run::fail. The reviewer judged it fine: the ordering comment explains why keep comes last.

Spec

  • The worktree is kept on any commit_and_push error, not only a failed push. This is deliberate. If merge --abort, add, the diff check or the commit fails, we don't know the work is safe on origin, so keeping it is the conservative choice.
  • The keep message is printed from Drop, not explicitly in fail(). It still prints the path, branch and head SHA as asked, and cleanup and reporting stay together in one place.
  • Re-running an issue after its worktree was kept hits a raw git error, because the worktree path and local branch already exist. The spec doesn't cover this, and the kept worktree is meant to be removed by hand. Friendlier handling is left for a follow-up issue.
  • Scope notes: the reworded push warning, the new pushing issue-N line on the Failed run path, and every git.run error now including stdout. All are kept. They follow from sending both pushes through Worktree::push and from the stdout fix, and nothing parses git error text.

Closes #55

🤖 Generated with Claude Code

JacobStephens2 and others added 4 commits September 25, 2026 20:14
Every push thirdshift makes now skips the target repo's hooks. A failed
git command reports the last lines of its stdout as well as its stderr,
so a pre-push hook's explanation reaches the user. When the Failed run
path can't push, the worktree and local Issue branch are kept and stderr
names the branch, head commit and worktree path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep the worktree's kept line factual and state the reason where the push
failed, tidy names and comments, and test an unreachable origin too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README step 5-7: keep this branch's note that the push skips the repo's
git hooks and the Failed-run exception to cleanup, alongside main's note
that a Base branch moving during CI is merged again, at most 3 times.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 0165322 into main Sep 26, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-55 branch September 26, 2026 03:11
@JacobStephens2 JacobStephens2 mentioned this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A pre-push hook in the target repo blocks every push, hides its reason, and the branch is then deleted

1 participant