@@ -126,15 +126,14 @@ export const fetchRepository = async (
126126
127127 // Update HEAD to match the remote's default branch. This handles the case where the remote's
128128 // default branch changes.
129- const remoteHead = await git . raw ( [ 'ls-remote' , '--symref' , cloneUrl , 'HEAD' ] ) ;
130- const match = remoteHead . match ( / ^ r e f : r e f s \/ h e a d s \/ ( \S + ) \s + H E A D / m) ;
131- if ( match ) {
132- const defaultBranch = match [ 1 ] ;
133- await git . raw ( [ 'symbolic-ref' , 'HEAD' , `refs/heads/${ defaultBranch } ` ] ) ;
134- return defaultBranch ;
135- }
129+ const remoteDefaultBranch = await getRemoteDefaultBranch ( {
130+ path,
131+ cloneUrl,
132+ } ) ;
136133
137- return undefined ;
134+ if ( remoteDefaultBranch ) {
135+ await git . raw ( [ 'symbolic-ref' , 'HEAD' , `refs/heads/${ remoteDefaultBranch } ` ] ) ;
136+ }
138137 } catch ( error : unknown ) {
139138 const baseLog = `Failed to fetch repository: ${ path } ` ;
140139 if ( env . SOURCEBOT_LOG_LEVEL !== "debug" ) {
@@ -310,3 +309,63 @@ export const getCommitHashForRefName = async ({
310309 return undefined ;
311310 }
312311}
312+
313+ /**
314+ * Gets the default branch name from the remote repository by querying what
315+ * the remote's HEAD symbolic ref points to.
316+ *
317+ * This is useful for detecting when a remote repository's default branch has
318+ * changed (e.g., from "master" to "main").
319+ *
320+ * @returns The branch name (e.g., "main", "master") or undefined if it cannot be determined
321+ */
322+ export const getRemoteDefaultBranch = async ( {
323+ path,
324+ cloneUrl,
325+ } : {
326+ path : string ,
327+ cloneUrl : string ,
328+ } ) => {
329+ const git = createGitClientForPath ( path ) ;
330+ try {
331+ const remoteHead = await git . raw ( [ 'ls-remote' , '--symref' , cloneUrl , 'HEAD' ] ) ;
332+ const match = remoteHead . match ( / ^ r e f : r e f s \/ h e a d s \/ ( \S + ) \s + H E A D / m) ;
333+ if ( match ) {
334+ return match [ 1 ] ;
335+ }
336+ } catch ( error : unknown ) {
337+ // Avoid printing error here since cloneUrl may contain credentials.
338+ console . error ( `Failed to get remote default branch for repository: ${ path } ` ) ;
339+ return undefined ;
340+ }
341+ }
342+
343+ /**
344+ * Gets the branch name that the local HEAD symbolic ref points to.
345+ *
346+ * In a git repository, HEAD is typically a symbolic reference that points to
347+ * a branch (e.g., refs/heads/main). This function resolves that symbolic ref
348+ * and returns just the branch name.
349+ *
350+ * @returns The branch name (e.g., "main", "master") or undefined if HEAD is not a symbolic ref
351+ */
352+ export const getLocalDefaultBranch = async ( {
353+ path,
354+ } : {
355+ path : string ,
356+ } ) => {
357+ const git = createGitClientForPath ( path ) ;
358+
359+ try {
360+ const ref = await git . raw ( [ 'symbolic-ref' , 'HEAD' ] ) ;
361+ // Returns something like "refs/heads/main\n", so trim and remove prefix
362+ const trimmed = ref . trim ( ) ;
363+ const match = trimmed . match ( / ^ r e f s \/ h e a d s \/ ( .+ ) $ / ) ;
364+ if ( match ) {
365+ return match [ 1 ] ;
366+ }
367+ } catch ( error : unknown ) {
368+ console . error ( `Failed to get local default branch for repository: ${ path } ` ) ;
369+ return undefined ;
370+ }
371+ }
0 commit comments