perf(git-sync): depth=1 fetch and skip fetch when remote is unchanged
Build and Deploy Verso / deploy (push) Successful in 1m28s
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user