-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: persist issue branches for reuse on subsequent invocations #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Check for existing Claude branches before creating new ones for issues - Skip automatic branch deletion for issue branches to allow reuse - Add context awareness so Claude knows when reusing an existing branch - Set IS_REUSED_BRANCH output for tracking branch reuse status This change allows users to iteratively ask Claude to make changes on the same branch before creating a PR, as requested in issue #103. Co-authored-by: ashwin-ant <[email protected]>
Claude finished @ashwin-ant's task —— View job PR Review: Branch Persistence FeatureI've completed my review of this PR that implements branch persistence for issue branches. Here's my comprehensive feedback: 📋 Review SummaryReview Tasks:
✅ What This PR Does Well
🔍 Code Quality Assessment
|
src/github/operations/branch.ts
Outdated
const { data: branches } = await octokits.rest.repos.listBranches({ | ||
owner, | ||
repo, | ||
per_page: 100, | ||
}); | ||
|
||
// Look for existing branches with pattern claude/issue-{entityNumber}-* | ||
const existingBranch = branches.find(branch => | ||
branch.name.startsWith(`claude/issue-${entityNumber}-`) | ||
); | ||
|
||
if (existingBranch) { | ||
branchToUse = existingBranch.name; | ||
isReusedBranch = true; | ||
console.log(`Found existing Claude branch for issue #${entityNumber}: ${branchToUse}`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work for repos with lots of branches. Let's do a graphql query so that we can filter on prefixes
query($owner: String!, $repo: String!, $prefix: String!) {
repository(owner: $owner, name: $repo) {
refs(refPrefix: "refs/heads/", query: $prefix, first: 100) {
nodes {
name
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @ashwin-ant's task —— View job
✅ Implemented GraphQL Branch Query
I've successfully replaced the REST API call with the suggested GraphQL query for more efficient branch filtering.
Completed Tasks:
- Read current implementation in
branch.ts
- Implement GraphQL query to replace REST API call
- Update response handling for GraphQL format
- Run tests to ensure functionality (all 175 tests passing)
- Commit changes
What Changed:
- Replaced
octokits.rest.repos.listBranches()
with a GraphQL query that filters branches by prefix - The GraphQL query uses the
refs
field with aquery
parameter to efficiently filter branches - This solves the limitation where repositories with >100 branches could miss existing Claude branches
- The new implementation is more performant as it filters on the server side
The change maintains backward compatibility while significantly improving branch discovery for large repositories.
…rformance - Replaced REST API listBranches call with GraphQL query that filters branches by prefix - This fixes the issue where repositories with >100 branches could miss existing Claude branches - The GraphQL query directly filters by prefix, making it more efficient and accurate Co-authored-by: ashwin-ant <[email protected]>
@claude review this again, any edge cases? |
Claude finished @ashwin-ant's task —— View job Edge Case Analysis for Branch Persistence FeatureAfter reviewing the updated implementation with GraphQL, here are the edge cases I've identified: Review Tasks:
🚨 Critical Edge Cases Found1. Race Condition: Concurrent Claude InvocationsScenario: Two users trigger Claude on the same issue within seconds of each other. Problem: Both invocations could run the GraphQL query at nearly the same time, both find no existing branch, and both try to create a new branch with different timestamps. Impact:
Fix needed: Add retry logic with branch existence check before creation. 2. Multiple Existing Branches SelectionCurrent code (line 119-120): // Use the first matching branch (could be sorted by date in future)
branchToUse = branches[0].name; Problem: If multiple branches exist (e.g., from failed cleanups or manual branch creation), the selection is arbitrary - it picks whichever GitHub returns first, which may not be the most recent or most relevant. Scenarios:
3. Orphaned Work DetectionScenario: Claude is invoked on an issue, makes commits, but the user never creates a PR. Later, Claude is invoked again. Problem: The reused branch message only says "This branch was reused from a previous Claude invocation" but doesn't indicate:
Impact: Claude might make conflicting changes without awareness of previous work. 4. Branch Deletion Edge CasesScenario: User manually deletes the Claude branch while Claude is working. Current behavior:
5. GraphQL Query LimitationsThe query (lines 92-102): refs(refPrefix: "refs/heads/", query: $prefix, first: 100) Problems:
6. Base Branch DivergenceScenario:
Impact: Merge conflicts become likely, and Claude lacks context about why. 7. Partial State RecoveryScenario: Previous Claude invocation crashed mid-operation, leaving:
Problem: New invocation has no way to detect or recover from this state. 8. Branch Protection RulesScenario: Repository has branch protection rules that prevent direct pushes to branches matching Impact:
9. Stale Branch AccumulationProblem: Issue branches are never cleaned up, even after issues are closed. Long-term impact:
10. Network Timeout During Branch CheckCurrent code (line 124-127): } catch (error) {
console.error("Error checking for existing branches:", error);
// Continue with new branch creation if check fails
} Problem: Network timeout is treated the same as "no branches found", potentially creating duplicate branches. 🔧 Recommendations for Robustness
const maxRetries = 3;
for (let i = 0; i < maxRetries; i++) {
try {
await octokits.rest.git.createRef(...);
break;
} catch (error) {
if (i === maxRetries - 1) throw error;
// Check if branch was created by another process
const exists = await checkBranchExists(branchName);
if (exists) {
branchToUse = branchName;
isReusedBranch = true;
break;
}
}
}
const existingBranches = branches
.filter(branch => branch.name.startsWith(`claude/issue-${entityNumber}-`))
.sort((a, b) => b.name.localeCompare(a.name)); // Most recent first
// After checking out existing branch
const status = await $`git status --porcelain`;
if (status.stdout) {
console.warn("Warning: Reused branch has uncommitted changes");
}
const behind = await $`git rev-list --count HEAD..origin/${sourceBranch}`;
console.log(`Branch is ${behind.stdout.trim()} commits behind ${sourceBranch}`);
✅ SummaryThe GraphQL implementation successfully addresses the performance issue, but several edge cases remain that could cause failures or confusion. The most critical are:
These edge cases are particularly important because the feature's goal is to improve iterative workflows, but these issues could actually make iteration more confusing. |
+1 |
This PR addresses issue #103
This allows users to iteratively ask Claude to make changes on the same branch before creating a PR.
Generated with Claude Code