perf(git-sync): depth=1 fetch and skip fetch when remote is unchanged
Build and Deploy Verso / deploy (push) Successful in 1m28s

--depth=1 limits fetch to only the latest commit's tree objects instead
of the full history, dramatically reducing fetch time on repos with
many commits. A git ls-remote check before fetching skips the fetch
entirely when local HEAD already matches remote HEAD, which is the
common case after a push with no external changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
claude
2026-06-24 11:07:18 +00:00
parent 930f934e31
commit 57a1ce4f14
@@ -169,13 +169,25 @@ async function pushToRemote(
// objects are sent — git handles the diff automatically.
const repoDir = await ensureRepo(projectId, remoteUrl, branch)
// Fetch remote state (trees only, no blobs) to preserve files outside the managed area.
// read-tree updates the index without checking out any files, so no blobs are downloaded.
// Fetch remote state (trees only, latest commit only) to preserve files outside the managed area.
// Skipped when remote HEAD already matches local HEAD (common case: re-push without external changes).
try {
await spawnGit(['fetch', '--filter=blob:none', 'origin', branch], repoDir)
// --mixed: moves HEAD to FETCH_HEAD + updates index, no working tree change.
// This makes FETCH_HEAD the parent of our next commit so the diff only shows subPath changes.
await spawnGit(['reset', '--mixed', 'FETCH_HEAD'], repoDir)
let needsFetch = true
try {
const remoteRef = await spawnGitOutput(['ls-remote', 'origin', branch], repoDir)
const remoteHead = remoteRef.split(/\s/)[0].trim()
const localHead = await spawnGitOutput(['rev-parse', 'HEAD'], repoDir)
needsFetch = remoteHead !== localHead
} catch {
// Can't compare — proceed with fetch
}
if (needsFetch) {
// --depth=1 --filter=blob:none: only the latest commit's tree objects, no blobs
await spawnGit(['fetch', '--filter=blob:none', '--depth=1', 'origin', branch], repoDir)
// --mixed: moves HEAD to FETCH_HEAD + updates index, no working tree change.
// This makes FETCH_HEAD the parent of our next commit so the diff only shows subPath changes.
await spawnGit(['reset', '--mixed', 'FETCH_HEAD'], repoDir)
}
} catch {
// Empty remote or first push — proceed with empty index
}
@@ -256,8 +268,8 @@ async function pullFromRemote(projectId, remoteUrl, subPath, branch, userId) {
logger.debug({ projectId, subPath }, 'git sync: fetching remote for pull')
const repoDir = await ensureRepo(projectId, remoteUrl, branch)
// Fetch with blob filter only trees/commits downloaded upfront
await spawnGit(['fetch', '--filter=blob:none', 'origin', branch], repoDir)
// depth=1 + blob filter: only the latest commit's tree objects, no blobs upfront
await spawnGit(['fetch', '--filter=blob:none', '--depth=1', 'origin', branch], repoDir)
if (subPath) {
// Checkout only the subPath directory — triggers lazy blob fetch for those files only