Skip to content

Commit f53c2a2

Browse files
improvements
1 parent ca4d640 commit f53c2a2

File tree

2 files changed

+73
-14
lines changed

2 files changed

+73
-14
lines changed

packages/backend/src/git.ts

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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(/^ref: refs\/heads\/(\S+)\s+HEAD/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(/^ref: refs\/heads\/(\S+)\s+HEAD/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(/^refs\/heads\/(.+)$/);
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+
}

packages/backend/src/repoIndexManager.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Job, Queue, ReservedJob, Worker } from "groupmq";
88
import { Redis } from 'ioredis';
99
import micromatch from 'micromatch';
1010
import { GROUPMQ_WORKER_STOP_GRACEFUL_TIMEOUT_MS, INDEX_CACHE_DIR } from './constants.js';
11-
import { cloneRepository, fetchRepository, getBranches, getCommitHashForRefName, getTags, isPathAValidGitRepoRoot, unsetGitConfig, upsertGitConfig } from './git.js';
11+
import { cloneRepository, fetchRepository, getBranches, getCommitHashForRefName, getLocalDefaultBranch, getTags, isPathAValidGitRepoRoot, unsetGitConfig, upsertGitConfig } from './git.js';
1212
import { captureEvent } from './posthog.js';
1313
import { PromClient } from './promClient.js';
1414
import { RepoWithConnections, Settings } from "./types.js";
@@ -337,8 +337,6 @@ export class RepoIndexManager {
337337
}
338338
}
339339

340-
let defaultBranch: string | undefined = undefined;
341-
342340
if (existsSync(repoPath) && !isReadOnly) {
343341
// @NOTE: in #483, we changed the cloning method s.t., we _no longer_
344342
// write the clone URL (which could contain a auth token) to the
@@ -353,7 +351,7 @@ export class RepoIndexManager {
353351
});
354352

355353
logger.info(`Fetching ${repo.name} (id: ${repo.id})...`);
356-
const { durationMs, data: remoteDefaultBranch } = await measure(() => fetchRepository({
354+
const { durationMs } = await measure(() => fetchRepository({
357355
cloneUrl: cloneUrlMaybeWithToken,
358356
authHeader,
359357
path: repoPath,
@@ -366,8 +364,6 @@ export class RepoIndexManager {
366364

367365
process.stdout.write('\n');
368366
logger.info(`Fetched ${repo.name} (id: ${repo.id}) in ${fetchDuration_s}s`);
369-
370-
defaultBranch = remoteDefaultBranch;
371367
} else if (!isReadOnly) {
372368
logger.info(`Cloning ${repo.name} (id: ${repo.id})...`);
373369

@@ -397,6 +393,10 @@ export class RepoIndexManager {
397393
});
398394
}
399395

396+
const defaultBranch = await getLocalDefaultBranch({
397+
path: repoPath,
398+
});
399+
400400
let revisions = defaultBranch ? [defaultBranch] : ['HEAD'];
401401

402402
if (metadata.branches) {

0 commit comments

Comments
 (0)