diff --git a/CHANGELOG.md b/CHANGELOG.md index c1fac5fb..153f0ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v2.9.3 +- [Fix change detection when base is a tag](https://github.com/dorny/paths-filter/pull/78) + ## v2.9.2 - [Fix fetching git history](https://github.com/dorny/paths-filter/pull/75) diff --git a/dist/index.js b/dist/index.js index e79004f6..e516c4fc 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3866,15 +3866,26 @@ async function getChangesOnHead() { } exports.getChangesOnHead = getChangesOnHead; async function getChangesSinceMergeBase(base, ref, initialFetchDepth) { - const baseRef = `remotes/origin/${base}`; + let baseRef; async function hasMergeBase() { - return (await exec_1.default('git', ['merge-base', baseRef, ref], { ignoreReturnCode: true })).code === 0; + return (baseRef !== undefined && (await exec_1.default('git', ['merge-base', baseRef, ref], { ignoreReturnCode: true })).code === 0); } let noMergeBase = false; - core.startGroup(`Searching for merge-base ${baseRef}...${ref}`); + core.startGroup(`Searching for merge-base ${base}...${ref}`); try { + baseRef = await getFullRef(base); if (!(await hasMergeBase())) { - await exec_1.default('git', ['fetch', `--depth=${initialFetchDepth}`, 'origin', base, ref]); + await exec_1.default('git', ['fetch', '--no-tags', `--depth=${initialFetchDepth}`, 'origin', base, ref]); + if (baseRef === undefined) { + baseRef = await getFullRef(base); + if (baseRef === undefined) { + await exec_1.default('git', ['fetch', '--tags', `--depth=1`, 'origin', base, ref]); + baseRef = await getFullRef(base); + if (baseRef === undefined) { + throw new Error(`Could not determine what is ${base} - fetch works but it's not a branch or tag`); + } + } + } let depth = initialFetchDepth; let lastCommitCount = await getCommitCount(); while (!(await hasMergeBase())) { @@ -3897,16 +3908,17 @@ async function getChangesSinceMergeBase(base, ref, initialFetchDepth) { finally { core.endGroup(); } + let diffArg = `${baseRef}...${ref}`; if (noMergeBase) { - core.warning('No merge base found - all files will be listed as added'); - return await listAllFilesAsAdded(); + core.warning('No merge base found - change detection will use direct .. comparison'); + diffArg = `${baseRef}..${ref}`; } - // Get changes introduced on HEAD compared to ref - core.startGroup(`Change detection ${baseRef}...${ref}`); + // Get changes introduced on ref compared to base + core.startGroup(`Change detection ${diffArg}`); let output = ''; try { // Three dots '...' change detection - finds merge-base and compares against it - output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}...${ref}`])).stdout; + output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', diffArg])).stdout; } finally { fixStdOutNullTermination(); @@ -3994,6 +4006,24 @@ async function getCommitCount() { const count = parseInt(output); return isNaN(count) ? 0 : count; } +async function getFullRef(shortName) { + if (isGitSha(shortName)) { + return shortName; + } + const output = (await exec_1.default('git', ['show-ref', shortName], { ignoreReturnCode: true })).stdout; + const refs = output + .split(/\r?\n/g) + .map(l => { var _a, _b; return (_b = (_a = l.match(/refs\/.*$/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''; }) + .filter(l => l !== ''); + if (refs.length === 0) { + return undefined; + } + const remoteRef = refs.find(ref => ref.startsWith('refs/remotes/origin/')); + if (remoteRef) { + return remoteRef; + } + return refs[0]; +} function fixStdOutNullTermination() { // Previous command uses NULL as delimiters and output is printed to stdout. // We have to make sure next thing written to stdout will start on new line. @@ -4743,7 +4773,7 @@ async function getChangedFilesFromGit(base, initialFetchDepth) { return await git.getChanges(baseSha); } // Changes introduced by current branch against the base branch - core.info(`Changes will be detected against the branch ${baseRef}`); + core.info(`Changes will be detected against ${baseRef}`); return await git.getChangesSinceMergeBase(baseRef, ref, initialFetchDepth); } // Uses github REST api to get list of files changed in PR diff --git a/src/git.ts b/src/git.ts index 531c301f..97a4021c 100644 --- a/src/git.ts +++ b/src/git.ts @@ -55,17 +55,30 @@ export async function getChangesOnHead(): Promise { } export async function getChangesSinceMergeBase(base: string, ref: string, initialFetchDepth: number): Promise { - const baseRef = `remotes/origin/${base}` - + let baseRef: string | undefined async function hasMergeBase(): Promise { - return (await exec('git', ['merge-base', baseRef, ref], {ignoreReturnCode: true})).code === 0 + return ( + baseRef !== undefined && (await exec('git', ['merge-base', baseRef, ref], {ignoreReturnCode: true})).code === 0 + ) } let noMergeBase = false - core.startGroup(`Searching for merge-base ${baseRef}...${ref}`) + core.startGroup(`Searching for merge-base ${base}...${ref}`) try { + baseRef = await getFullRef(base) if (!(await hasMergeBase())) { - await exec('git', ['fetch', `--depth=${initialFetchDepth}`, 'origin', base, ref]) + await exec('git', ['fetch', '--no-tags', `--depth=${initialFetchDepth}`, 'origin', base, ref]) + if (baseRef === undefined) { + baseRef = await getFullRef(base) + if (baseRef === undefined) { + await exec('git', ['fetch', '--tags', `--depth=1`, 'origin', base, ref]) + baseRef = await getFullRef(base) + if (baseRef === undefined) { + throw new Error(`Could not determine what is ${base} - fetch works but it's not a branch or tag`) + } + } + } + let depth = initialFetchDepth let lastCommitCount = await getCommitCount() while (!(await hasMergeBase())) { @@ -88,17 +101,18 @@ export async function getChangesSinceMergeBase(base: string, ref: string, initia core.endGroup() } + let diffArg = `${baseRef}...${ref}` if (noMergeBase) { - core.warning('No merge base found - all files will be listed as added') - return await listAllFilesAsAdded() + core.warning('No merge base found - change detection will use direct .. comparison') + diffArg = `${baseRef}..${ref}` } - // Get changes introduced on HEAD compared to ref - core.startGroup(`Change detection ${baseRef}...${ref}`) + // Get changes introduced on ref compared to base + core.startGroup(`Change detection ${diffArg}`) let output = '' try { // Three dots '...' change detection - finds merge-base and compares against it - output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}...${ref}`])).stdout + output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', diffArg])).stdout } finally { fixStdOutNullTermination() core.endGroup() @@ -188,6 +202,29 @@ async function getCommitCount(): Promise { return isNaN(count) ? 0 : count } +async function getFullRef(shortName: string): Promise { + if (isGitSha(shortName)) { + return shortName + } + + const output = (await exec('git', ['show-ref', shortName], {ignoreReturnCode: true})).stdout + const refs = output + .split(/\r?\n/g) + .map(l => l.match(/refs\/.*$/)?.[0] ?? '') + .filter(l => l !== '') + + if (refs.length === 0) { + return undefined + } + + const remoteRef = refs.find(ref => ref.startsWith('refs/remotes/origin/')) + if (remoteRef) { + return remoteRef + } + + return refs[0] +} + function fixStdOutNullTermination(): void { // Previous command uses NULL as delimiters and output is printed to stdout. // We have to make sure next thing written to stdout will start on new line. diff --git a/src/main.ts b/src/main.ts index e3d6ebc4..de33bc26 100644 --- a/src/main.ts +++ b/src/main.ts @@ -121,7 +121,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}`) + core.info(`Changes will be detected against ${baseRef}`) return await git.getChangesSinceMergeBase(baseRef, ref, initialFetchDepth) }