From 3be8c93277e793fe1248dfc6a1e530f398aba1c4 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Mon, 8 Mar 2021 15:09:07 +0100 Subject: [PATCH] Fix fetching git history + fallback to unshallow repo --- src/git.ts | 85 ++++++++++++++++++++++++++--------------------------- src/main.ts | 10 +++---- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/git.ts b/src/git.ts index f34a5cc..0895313 100644 --- a/src/git.ts +++ b/src/git.ts @@ -18,20 +18,20 @@ export async function getChangesInLastCommit(): Promise { return parseGitDiffOutput(output) } -export async function getChanges(ref: string): Promise { - if (!(await hasCommit(ref))) { +export async function getChanges(baseRef: string): Promise { + if (!(await hasCommit(baseRef))) { // Fetch single commit - core.startGroup(`Fetching ${ref} from origin`) - await exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', ref]) + core.startGroup(`Fetching ${baseRef} from origin`) + await exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', baseRef]) core.endGroup() } // Get differences between ref and HEAD - core.startGroup(`Change detection ${ref}..HEAD`) + core.startGroup(`Change detection ${baseRef}..HEAD`) let output = '' try { // Two dots '..' change detection - directly compares two versions - output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}..HEAD`])).stdout + output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}..HEAD`])).stdout } finally { fixStdOutNullTermination() core.endGroup() @@ -54,50 +54,49 @@ export async function getChangesOnHead(): Promise { return parseGitDiffOutput(output) } -export async function getChangesSinceMergeBase(ref: string, initialFetchDepth: number): Promise { - if (!(await hasCommit(ref))) { - // Fetch and add base branch - core.startGroup(`Fetching ${ref}`) - try { - await exec('git', ['fetch', `--depth=${initialFetchDepth}`, '--no-tags', 'origin', `${ref}:${ref}`]) - } finally { - core.endGroup() - } - } - +export async function getChangesSinceMergeBase( + baseRef: string, + ref: string, + initialFetchDepth: number +): Promise { async function hasMergeBase(): Promise { - return (await exec('git', ['merge-base', ref, 'HEAD'], {ignoreReturnCode: true})).code === 0 + return (await exec('git', ['merge-base', baseRef, ref], {ignoreReturnCode: true})).code === 0 } - async function countCommits(): Promise { - return (await getNumberOfCommits('HEAD')) + (await getNumberOfCommits(ref)) - } - - core.startGroup(`Searching for merge-base with ${ref}`) - // Fetch more commits until merge-base is found - if (!(await hasMergeBase())) { - let deepen = initialFetchDepth - let lastCommitsCount = await countCommits() - do { - await exec('git', ['fetch', `--deepen=${deepen}`, '--no-tags']) - const count = await countCommits() - if (count <= lastCommitsCount) { - core.info('No merge base found - all files will be listed as added') - core.endGroup() - return await listAllFilesAsAdded() + let noMergeBase = false + core.startGroup(`Searching for merge-base ${baseRef}...${ref}`) + try { + let lastCommitCount = await getCommitCount() + let depth = Math.max(lastCommitCount * 2, initialFetchDepth) + while (!(await hasMergeBase())) { + await exec('git', ['fetch', `--depth=${depth}`, 'origin', `${baseRef}:${baseRef}`, `${ref}:${ref}`]) + const commitCount = await getCommitCount() + if (commitCount === lastCommitCount) { + core.info('No more commits were fetched') + core.info('Last attempt will be to fetch full history') + await exec('git', ['fetch', '--unshallow']) + if (!(await hasMergeBase())) { + noMergeBase = true + } + break } - lastCommitsCount = count - deepen = Math.min(deepen * 2, Number.MAX_SAFE_INTEGER) - } while (!(await hasMergeBase())) + depth = Math.min(depth * 2, Number.MAX_SAFE_INTEGER) + } + } finally { + core.endGroup() + } + + if (noMergeBase) { + core.warning('No merge base found - all files will be listed as added') + return await listAllFilesAsAdded() } - core.endGroup() // Get changes introduced on HEAD compared to ref - core.startGroup(`Change detection ${ref}...HEAD`) + core.startGroup(`Change detection ${baseRef}...${ref}`) let output = '' try { // Three dots '...' change detection - finds merge-base and compares against it - output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}...HEAD`])).stdout + output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}...${ref}`])).stdout } finally { fixStdOutNullTermination() core.endGroup() @@ -150,7 +149,7 @@ export async function getCurrentRef(): Promise { return describe.stdout.trim() } - return (await exec('git', ['rev-parse', 'HEAD'])).stdout.trim() + return (await exec('git', ['rev-parse', HEAD])).stdout.trim() } finally { core.endGroup() } @@ -181,8 +180,8 @@ async function hasCommit(ref: string): Promise { } } -async function getNumberOfCommits(ref: string): Promise { - const output = (await exec('git', ['rev-list', `--count`, ref])).stdout +async function getCommitCount(): Promise { + const output = (await exec('git', ['rev-list', '--count', '--all'])).stdout const count = parseInt(output) return isNaN(count) ? 0 : count } diff --git a/src/main.ts b/src/main.ts index a54776f..395ab69 100644 --- a/src/main.ts +++ b/src/main.ts @@ -80,7 +80,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): const beforeSha = github.context.eventName === 'push' ? (github.context.payload as Webhooks.WebhookPayloadPush).before : null - const pushRef = + const ref = git.getShortName(github.context.ref) || (core.warning(`'ref' field is missing in event payload - using current branch, tag or commit SHA`), await git.getCurrentRef()) @@ -93,11 +93,11 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): } const isBaseRefSha = git.isGitSha(baseRef) - const isBaseSameAsPush = baseRef === pushRef + const isBaseRefSameAsRef = baseRef === ref // If base is commit SHA we will do comparison against the referenced commit // Or if base references same branch it was pushed to, we will do comparison against the previously pushed commit - if (isBaseRefSha || isBaseSameAsPush) { + if (isBaseRefSha || isBaseRefSameAsRef) { if (!isBaseRefSha && !beforeSha) { core.warning(`'before' field is missing in event payload - changes will be detected from last commit`) return await git.getChangesInLastCommit() @@ -109,7 +109,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): if (baseSha === git.NULL_SHA) { if (defaultRef && baseRef !== defaultRef) { core.info(`First push of a branch detected - changes will be detected against the default branch ${defaultRef}`) - return await git.getChangesSinceMergeBase(defaultRef, initialFetchDepth) + return await git.getChangesSinceMergeBase(defaultRef, ref, initialFetchDepth) } else { core.info('Initial push detected - all files will be listed as added') return await git.listAllFilesAsAdded() @@ -122,7 +122,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): // Changes introduced by current branch against the base branch core.info(`Changes will be detected against the branch ${baseRef}`) - return await git.getChangesSinceMergeBase(baseRef, initialFetchDepth) + return await git.getChangesSinceMergeBase(baseRef, ref, initialFetchDepth) } // Uses github REST api to get list of files changed in PR