feat: defer remote branch creation until first commit (#244)

* feat: defer remote branch creation until first commit

- For commit signing: branches are created remotely by github-file-ops-server on first commit
- For non-signing: branches are created locally with 'git checkout -b' and pushed when needed
- Consolidated duplicate branch creation logic in github-file-ops-server into a shared helper function
- Claude is unaware of these implementation details and simply sees it's on the correct branch
- No branch links are shown in initial comments since branches don't exist remotely yet

* fix: prevent broken branch links in final comment update

- Check if branch exists remotely before adding branch link
- Only add branch links for branches that actually exist on GitHub
- Add test coverage for non-existent remote branches
- Fixes issue where users would see broken branch links for local-only branches

* fix: don't show branch name in comment header when branch doesn't exist remotely

- Only pass branchName to updateCommentBody when branchLink exists
- Prevents showing branch names for branches that only exist locally
- Add test to verify branch name is not shown when branch doesn't exist

* tmp
This commit is contained in:
Ashwin Bhat
2025-07-10 12:57:15 -07:00
committed by GitHub
parent eda5af4e69
commit cefe963a6b
8 changed files with 249 additions and 75 deletions

View File

@@ -21,6 +21,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
const createMockOctokit = (
compareResponse?: any,
deleteRefError?: Error,
branchExists: boolean = true,
): Octokits => {
return {
rest: {
@@ -28,6 +29,14 @@ describe("checkAndCommitOrDeleteBranch", () => {
compareCommitsWithBasehead: async () => ({
data: compareResponse || { total_commits: 0 },
}),
getBranch: async () => {
if (!branchExists) {
const error: any = new Error("Not Found");
error.status = 404;
throw error;
}
return { data: {} };
},
},
git: {
deleteRef: async () => {
@@ -102,6 +111,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
compareCommitsWithBasehead: async () => {
throw new Error("API error");
},
getBranch: async () => ({ data: {} }), // Branch exists
},
git: {
deleteRef: async () => ({ data: {} }),
@@ -123,7 +133,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
`\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101_123456)`,
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
"Error checking for commits on Claude branch:",
"Error comparing commits on Claude branch:",
expect.any(Error),
);
});
@@ -148,4 +158,30 @@ describe("checkAndCommitOrDeleteBranch", () => {
deleteError,
);
});
test("should return no branch link when branch doesn't exist remotely", async () => {
const mockOctokit = createMockOctokit(
{ total_commits: 0 },
undefined,
false, // branch doesn't exist
);
const result = await checkAndCommitOrDeleteBranch(
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"main",
false,
);
expect(result.shouldDeleteBranch).toBe(false);
expect(result.branchLink).toBe("");
expect(consoleLogSpy).toHaveBeenCalledWith(
"Branch claude/issue-123-20240101_123456 does not exist remotely",
);
expect(consoleLogSpy).toHaveBeenCalledWith(
"Branch claude/issue-123-20240101_123456 does not exist remotely, no branch link will be added",
);
});
});