1- import { existsSync , mkdirSync , rmSync } from 'node:fs' ;
1+ import { existsSync , mkdirSync , rmSync , readdirSync } from 'node:fs' ;
22import { join } from 'node:path' ;
33import { createHash } from 'node:crypto' ;
44import { homedir , tmpdir } from 'node:os' ;
@@ -21,10 +21,35 @@ function cacheKey(url: string, branch: string): string {
2121 return createHash ( 'sha256' ) . update ( `${ url } #${ branch } ` ) . digest ( 'hex' ) . slice ( 0 , 16 ) ;
2222}
2323
24+ function isDirEmpty ( dir : string ) : boolean {
25+ try {
26+ return readdirSync ( dir ) . length === 0 ;
27+ } catch {
28+ return true ;
29+ }
30+ }
31+
32+ function detectDefaultBranch ( url : string ) : string {
33+ try {
34+ const output = execSync ( `git ls-remote --symref ${ url } HEAD` , {
35+ encoding : 'utf-8' ,
36+ stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
37+ timeout : 15_000 ,
38+ } ) ;
39+ // Parse: ref: refs/heads/master HEAD
40+ const match = output . match ( / r e f : r e f s \/ h e a d s \/ ( \S + ) \s + H E A D / ) ;
41+ if ( match ?. [ 1 ] ) return match [ 1 ] ;
42+ } catch {
43+ // fallback
44+ }
45+ return 'main' ;
46+ }
47+
2448export function resolveRepo ( url : string , options : ResolveRepoOptions = { } ) : ResolveRepoResult {
25- const branch = options . branch ?? 'main' ;
49+ const requestedBranch = options . branch ?? 'main' ;
2650
2751 if ( options . noCache ) {
52+ const branch = requestedBranch === 'main' ? detectDefaultBranch ( url ) : requestedBranch ;
2853 const dir = join ( tmpdir ( ) , `gitagent-${ cacheKey ( url , branch ) } -${ Date . now ( ) } ` ) ;
2954 cloneRepo ( url , branch , dir ) ;
3055 return {
@@ -35,21 +60,33 @@ export function resolveRepo(url: string, options: ResolveRepoOptions = {}): Reso
3560 } ;
3661 }
3762
38- const hash = cacheKey ( url , branch ) ;
63+ const hash = cacheKey ( url , requestedBranch ) ;
3964 const dir = join ( CACHE_BASE , hash ) ;
4065
41- if ( existsSync ( dir ) && ! options . refresh ) {
66+ // If cached dir exists, is non-empty, and no refresh requested — use it
67+ if ( existsSync ( dir ) && ! isDirEmpty ( dir ) && ! options . refresh ) {
4268 return { dir } ;
4369 }
4470
45- if ( existsSync ( dir ) && options . refresh ) {
46- // Nuke cached clone and re-clone fresh to avoid divergent branch issues
71+ // Clean up stale/empty cache dir
72+ if ( existsSync ( dir ) ) {
4773 rmSync ( dir , { recursive : true , force : true } ) ;
48- cloneRepo ( url , branch , dir ) ;
49- return { dir } ;
5074 }
5175
52- cloneRepo ( url , branch , dir ) ;
76+ // Try cloning with requested branch first, fall back to auto-detect
77+ try {
78+ cloneRepo ( url , requestedBranch , dir ) ;
79+ } catch {
80+ // Branch not found — auto-detect default branch and retry
81+ if ( existsSync ( dir ) ) rmSync ( dir , { recursive : true , force : true } ) ;
82+ const detectedBranch = detectDefaultBranch ( url ) ;
83+ if ( detectedBranch !== requestedBranch ) {
84+ cloneRepo ( url , detectedBranch , dir ) ;
85+ } else {
86+ throw new Error ( `Could not clone ${ url } — branch "${ requestedBranch } " not found` ) ;
87+ }
88+ }
89+
5390 return { dir } ;
5491}
5592
0 commit comments