mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 02:20:38 +09:00
fix: auto-detect default branch for worktree creation when baseRef not configured (#2463)
* fix: auto-detect default branch for worktree creation when baseRef not configured When creating git worktrees, if no explicit baseRef is configured in the project workspace strategy and no repoRef is set, the system now auto-detects the repository's default branch instead of blindly falling back to "HEAD". Detection strategy: 1. Check refs/remotes/origin/HEAD (set by git clone / remote set-head) 2. Fall back to probing refs/remotes/origin/main, then origin/master 3. Final fallback: HEAD (preserves existing behavior) This prevents failures like "fatal: invalid reference: main" when a project's workspace strategy has no baseRef and the repo uses a non-standard default branch name. Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix: address Greptile review - fix misleading comment and add symbolic-ref test - Corrected comment to clarify that the existing test exercises the heuristic fallback path (not symbolic-ref) - Added new test case that explicitly sets refs/remotes/origin/HEAD via `git remote set-head` to exercise the symbolic-ref code path Co-Authored-By: Paperclip <noreply@paperclip.ing> --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
056a5ee32a
commit
1e24e6e84c
2 changed files with 147 additions and 1 deletions
|
|
@ -297,6 +297,32 @@ function gitErrorIncludes(error: unknown, needle: string) {
|
|||
return message.toLowerCase().includes(needle.toLowerCase());
|
||||
}
|
||||
|
||||
async function detectDefaultBranch(repoRoot: string): Promise<string | null> {
|
||||
// Try the explicit remote HEAD first (set by git clone or git remote set-head)
|
||||
try {
|
||||
const remoteHead = await runGit(
|
||||
["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"],
|
||||
repoRoot,
|
||||
);
|
||||
const branch = remoteHead?.startsWith("origin/") ? remoteHead.slice("origin/".length) : remoteHead;
|
||||
if (branch) return branch;
|
||||
} catch {
|
||||
// Not set — fall through to heuristic
|
||||
}
|
||||
|
||||
// Fallback: check for common default branch names on the remote
|
||||
for (const candidate of ["main", "master"]) {
|
||||
try {
|
||||
await runGit(["rev-parse", "--verify", `refs/remotes/origin/${candidate}`], repoRoot);
|
||||
return candidate;
|
||||
} catch {
|
||||
// Not found — try next
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function directoryExists(value: string) {
|
||||
return fs.stat(value).then((stats) => stats.isDirectory()).catch(() => false);
|
||||
}
|
||||
|
|
@ -601,7 +627,12 @@ export async function realizeExecutionWorkspace(input: {
|
|||
? resolveConfiguredPath(configuredParentDir, repoRoot)
|
||||
: path.join(repoRoot, ".paperclip", "worktrees");
|
||||
const worktreePath = path.join(worktreeParentDir, branchName);
|
||||
const baseRef = asString(rawStrategy.baseRef, input.base.repoRef ?? "HEAD");
|
||||
const configuredBaseRef = typeof rawStrategy.baseRef === "string" && rawStrategy.baseRef.length > 0
|
||||
? rawStrategy.baseRef
|
||||
: input.base.repoRef ?? null;
|
||||
const baseRef = configuredBaseRef
|
||||
?? await detectDefaultBranch(repoRoot)
|
||||
?? "HEAD";
|
||||
|
||||
await fs.mkdir(worktreeParentDir, { recursive: true });
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue