From e59197f91babb250583f997b89e8230af4019925 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Thu, 25 Mar 2021 23:34:50 +0100 Subject: [PATCH 1/5] Fix change detection when base is tag --- src/git.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/git.ts b/src/git.ts index 531c301f..4d07d769 100644 --- a/src/git.ts +++ b/src/git.ts @@ -55,17 +55,28 @@ 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}`) 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 +99,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 +200,29 @@ async function getCommitCount(): Promise { return isNaN(count) ? 0 : count } +async function getFullRef(shortName: string) { + if(isGitSha(shortName)) { + return shortName + } + + const remoteRef = `refs/remotes/origin/${shortName}` + const tagRef = `refs/tags/${shortName}` + const headRef = `refs/heads/${shortName}` + if (await verifyRef(remoteRef)) { + return remoteRef + } else if (await verifyRef(tagRef)) { + return tagRef + } else if (await verifyRef(headRef)) { + return headRef + } + + return undefined +} + +async function verifyRef(ref: string): Promise { + return (await exec('git', ['show-ref', '--verify', ref], {ignoreReturnCode: true})).code === 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. From 3d4a25053b505741f53334bafb951497a2cc5e80 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Thu, 25 Mar 2021 23:39:10 +0100 Subject: [PATCH 2/5] Update dist --- dist/index.js | 49 +++++++++++++++++++++++++++++++++++++++++-------- src/git.ts | 8 +++++--- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/dist/index.js b/dist/index.js index e79004f6..ec0889ed 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}`); 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,27 @@ async function getCommitCount() { const count = parseInt(output); return isNaN(count) ? 0 : count; } +async function getFullRef(shortName) { + if (isGitSha(shortName)) { + return shortName; + } + const remoteRef = `refs/remotes/origin/${shortName}`; + const tagRef = `refs/tags/${shortName}`; + const headRef = `refs/heads/${shortName}`; + if (await verifyRef(remoteRef)) { + return remoteRef; + } + else if (await verifyRef(tagRef)) { + return tagRef; + } + else if (await verifyRef(headRef)) { + return headRef; + } + return undefined; +} +async function verifyRef(ref) { + return (await exec_1.default('git', ['show-ref', '--verify', ref], { ignoreReturnCode: true })).code === 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. diff --git a/src/git.ts b/src/git.ts index 4d07d769..0c312140 100644 --- a/src/git.ts +++ b/src/git.ts @@ -57,7 +57,9 @@ export async function getChangesOnHead(): Promise { export async function getChangesSinceMergeBase(base: string, ref: string, initialFetchDepth: number): Promise { let baseRef: string | undefined async function hasMergeBase(): Promise { - return baseRef !== undefined && (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 @@ -200,8 +202,8 @@ async function getCommitCount(): Promise { return isNaN(count) ? 0 : count } -async function getFullRef(shortName: string) { - if(isGitSha(shortName)) { +async function getFullRef(shortName: string): Promise { + if (isGitSha(shortName)) { return shortName } From 6d8169070c8d8909cf087c81ce1f4120a0cfcbc5 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Thu, 25 Mar 2021 23:45:44 +0100 Subject: [PATCH 3/5] Improve logging --- dist/index.js | 4 ++-- src/git.ts | 2 +- src/main.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index ec0889ed..7ac8911a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3871,7 +3871,7 @@ async function getChangesSinceMergeBase(base, ref, initialFetchDepth) { 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())) { @@ -4776,7 +4776,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 0c312140..4831e83b 100644 --- a/src/git.ts +++ b/src/git.ts @@ -63,7 +63,7 @@ export async function getChangesSinceMergeBase(base: string, ref: string, initia } 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())) { 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) } From a6989ad59299fce51b6ce7850e7eaebdfce5ee59 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Fri, 26 Mar 2021 00:05:48 +0100 Subject: [PATCH 4/5] Get full ref name without multiple invocations to git show-ref --- dist/index.js | 25 +++++++++++-------------- src/git.ts | 26 +++++++++++++------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/dist/index.js b/dist/index.js index 7ac8911a..e516c4fc 100644 --- a/dist/index.js +++ b/dist/index.js @@ -4010,22 +4010,19 @@ async function getFullRef(shortName) { if (isGitSha(shortName)) { return shortName; } - const remoteRef = `refs/remotes/origin/${shortName}`; - const tagRef = `refs/tags/${shortName}`; - const headRef = `refs/heads/${shortName}`; - if (await verifyRef(remoteRef)) { - return remoteRef; - } - else if (await verifyRef(tagRef)) { - return tagRef; + 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; } - else if (await verifyRef(headRef)) { - return headRef; + const remoteRef = refs.find(ref => ref.startsWith('refs/remotes/origin/')); + if (remoteRef) { + return remoteRef; } - return undefined; -} -async function verifyRef(ref) { - return (await exec_1.default('git', ['show-ref', '--verify', ref], { ignoreReturnCode: true })).code === 0; + return refs[0]; } function fixStdOutNullTermination() { // Previous command uses NULL as delimiters and output is printed to stdout. diff --git a/src/git.ts b/src/git.ts index 4831e83b..97a4021c 100644 --- a/src/git.ts +++ b/src/git.ts @@ -207,22 +207,22 @@ async function getFullRef(shortName: string): Promise { return shortName } - const remoteRef = `refs/remotes/origin/${shortName}` - const tagRef = `refs/tags/${shortName}` - const headRef = `refs/heads/${shortName}` - if (await verifyRef(remoteRef)) { - return remoteRef - } else if (await verifyRef(tagRef)) { - return tagRef - } else if (await verifyRef(headRef)) { - return headRef + 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 } - return undefined -} + const remoteRef = refs.find(ref => ref.startsWith('refs/remotes/origin/')) + if (remoteRef) { + return remoteRef + } -async function verifyRef(ref: string): Promise { - return (await exec('git', ['show-ref', '--verify', ref], {ignoreReturnCode: true})).code === 0 + return refs[0] } function fixStdOutNullTermination(): void { From 5d414b88ab65a4abf46c20f8381ced818ffbb500 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Fri, 26 Mar 2021 00:14:59 +0100 Subject: [PATCH 5/5] Update CHANGELOG for v2.9.3 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) 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)