Skip to content

Commit 0aa1597

Browse files
authored
Merge pull request #74 from dorny/fix-searching-for-merge-base
Fix fetching git history + fallback to unshallow repo
2 parents 1cdd3bb + 46d2898 commit 0aa1597

File tree

6 files changed

+112
-99
lines changed

6 files changed

+112
-99
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## v2.9.1
4+
- [Fix fetching git history + fallback to unshallow repo](https://github.com/dorny/paths-filter/pull/74)
5+
36
## v2.9.0
47
- [Add list-files: csv format](https://github.com/dorny/paths-filter/pull/68)
58

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ For more information see [CHANGELOG](https://github.com/dorny/paths-filter/blob/
117117
# is found or there are no more commits in the history.
118118
# This option takes effect only when changes are detected
119119
# using git against base branch (feature branch workflow).
120-
# Default: 20
120+
# Default: 100
121121
initial-fetch-depth: ''
122122
123123
# Enables listing of files matching the filter:

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ inputs:
3838
until the merge-base is found or there are no more commits in the history.
3939
This option takes effect only when changes are detected using git against different base branch.
4040
required: false
41-
default: '10'
41+
default: '100'
4242
outputs:
4343
changes:
4444
description: JSON array with names of all filters matching any of changed files

dist/index.js

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3830,19 +3830,19 @@ async function getChangesInLastCommit() {
38303830
return parseGitDiffOutput(output);
38313831
}
38323832
exports.getChangesInLastCommit = getChangesInLastCommit;
3833-
async function getChanges(ref) {
3834-
if (!(await hasCommit(ref))) {
3833+
async function getChanges(baseRef) {
3834+
if (!(await hasCommit(baseRef))) {
38353835
// Fetch single commit
3836-
core.startGroup(`Fetching ${ref} from origin`);
3837-
await exec_1.default('git', ['fetch', '--depth=1', '--no-tags', 'origin', ref]);
3836+
core.startGroup(`Fetching ${baseRef} from origin`);
3837+
await exec_1.default('git', ['fetch', '--depth=1', '--no-tags', 'origin', baseRef]);
38383838
core.endGroup();
38393839
}
38403840
// Get differences between ref and HEAD
3841-
core.startGroup(`Change detection ${ref}..HEAD`);
3841+
core.startGroup(`Change detection ${baseRef}..HEAD`);
38423842
let output = '';
38433843
try {
38443844
// Two dots '..' change detection - directly compares two versions
3845-
output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}..HEAD`])).stdout;
3845+
output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}..HEAD`])).stdout;
38463846
}
38473847
finally {
38483848
fixStdOutNullTermination();
@@ -3865,47 +3865,51 @@ async function getChangesOnHead() {
38653865
return parseGitDiffOutput(output);
38663866
}
38673867
exports.getChangesOnHead = getChangesOnHead;
3868-
async function getChangesSinceMergeBase(ref, initialFetchDepth) {
3869-
if (!(await hasCommit(ref))) {
3870-
// Fetch and add base branch
3871-
core.startGroup(`Fetching ${ref}`);
3872-
try {
3873-
await exec_1.default('git', ['fetch', `--depth=${initialFetchDepth}`, '--no-tags', 'origin', `${ref}:${ref}`]);
3874-
}
3875-
finally {
3876-
core.endGroup();
3877-
}
3878-
}
3868+
async function getChangesSinceMergeBase(baseRef, ref, initialFetchDepth) {
38793869
async function hasMergeBase() {
3880-
return (await exec_1.default('git', ['merge-base', ref, 'HEAD'], { ignoreReturnCode: true })).code === 0;
3881-
}
3882-
async function countCommits() {
3883-
return (await getNumberOfCommits('HEAD')) + (await getNumberOfCommits(ref));
3884-
}
3885-
core.startGroup(`Searching for merge-base with ${ref}`);
3886-
// Fetch more commits until merge-base is found
3887-
if (!(await hasMergeBase())) {
3888-
let deepen = initialFetchDepth;
3889-
let lastCommitsCount = await countCommits();
3890-
do {
3891-
await exec_1.default('git', ['fetch', `--deepen=${deepen}`, '--no-tags']);
3892-
const count = await countCommits();
3893-
if (count <= lastCommitsCount) {
3894-
core.info('No merge base found - all files will be listed as added');
3895-
core.endGroup();
3896-
return await listAllFilesAsAdded();
3870+
return (await exec_1.default('git', ['merge-base', baseRef, ref], { ignoreReturnCode: true })).code === 0;
3871+
}
3872+
let noMergeBase = false;
3873+
core.startGroup(`Searching for merge-base ${baseRef}...${ref}`);
3874+
try {
3875+
let init = true;
3876+
let lastCommitCount = await getCommitCount();
3877+
let depth = Math.max(lastCommitCount * 2, initialFetchDepth);
3878+
while (!(await hasMergeBase())) {
3879+
if (init) {
3880+
await exec_1.default('git', ['fetch', `--depth=${depth}`, 'origin', `${baseRef}:${baseRef}`, `${ref}`]);
3881+
init = false;
3882+
}
3883+
else {
3884+
await exec_1.default('git', ['fetch', `--deepen=${depth}`, 'origin', baseRef, ref]);
38973885
}
3898-
lastCommitsCount = count;
3899-
deepen = Math.min(deepen * 2, Number.MAX_SAFE_INTEGER);
3900-
} while (!(await hasMergeBase()));
3886+
const commitCount = await getCommitCount();
3887+
if (commitCount === lastCommitCount) {
3888+
core.info('No more commits were fetched');
3889+
core.info('Last attempt will be to fetch full history');
3890+
await exec_1.default('git', ['fetch', '--unshallow']);
3891+
if (!(await hasMergeBase())) {
3892+
noMergeBase = true;
3893+
}
3894+
break;
3895+
}
3896+
depth = Math.min(depth * 2, Number.MAX_SAFE_INTEGER);
3897+
lastCommitCount = commitCount;
3898+
}
3899+
}
3900+
finally {
3901+
core.endGroup();
3902+
}
3903+
if (noMergeBase) {
3904+
core.warning('No merge base found - all files will be listed as added');
3905+
return await listAllFilesAsAdded();
39013906
}
3902-
core.endGroup();
39033907
// Get changes introduced on HEAD compared to ref
3904-
core.startGroup(`Change detection ${ref}...HEAD`);
3908+
core.startGroup(`Change detection ${baseRef}...${ref}`);
39053909
let output = '';
39063910
try {
39073911
// Three dots '...' change detection - finds merge-base and compares against it
3908-
output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}...HEAD`])).stdout;
3912+
output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}...${ref}`])).stdout;
39093913
}
39103914
finally {
39113915
fixStdOutNullTermination();
@@ -3956,7 +3960,7 @@ async function getCurrentRef() {
39563960
if (describe.code === 0) {
39573961
return describe.stdout.trim();
39583962
}
3959-
return (await exec_1.default('git', ['rev-parse', 'HEAD'])).stdout.trim();
3963+
return (await exec_1.default('git', ['rev-parse', exports.HEAD])).stdout.trim();
39603964
}
39613965
finally {
39623966
core.endGroup();
@@ -3988,8 +3992,8 @@ async function hasCommit(ref) {
39883992
core.endGroup();
39893993
}
39903994
}
3991-
async function getNumberOfCommits(ref) {
3992-
const output = (await exec_1.default('git', ['rev-list', `--count`, ref])).stdout;
3995+
async function getCommitCount() {
3996+
const output = (await exec_1.default('git', ['rev-list', '--count', '--all'])).stdout;
39933997
const count = parseInt(output);
39943998
return isNaN(count) ? 0 : count;
39953999
}
@@ -4676,7 +4680,7 @@ async function run() {
46764680
}
46774681
}
46784682
function isPathInput(text) {
4679-
return !text.includes('\n');
4683+
return !(text.includes('\n') || text.includes(':'));
46804684
}
46814685
function getConfigFileContent(configPath) {
46824686
if (!fs.existsSync(configPath)) {
@@ -4709,18 +4713,18 @@ async function getChangedFilesFromGit(base, initialFetchDepth) {
47094713
var _a;
47104714
const defaultRef = (_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.default_branch;
47114715
const beforeSha = github.context.eventName === 'push' ? github.context.payload.before : null;
4712-
const pushRef = git.getShortName(github.context.ref) ||
4716+
const ref = git.getShortName(github.context.ref) ||
47134717
(core.warning(`'ref' field is missing in event payload - using current branch, tag or commit SHA`),
47144718
await git.getCurrentRef());
47154719
const baseRef = git.getShortName(base) || defaultRef;
47164720
if (!baseRef) {
47174721
throw new Error("This action requires 'base' input to be configured or 'repository.default_branch' to be set in the event payload");
47184722
}
47194723
const isBaseRefSha = git.isGitSha(baseRef);
4720-
const isBaseSameAsPush = baseRef === pushRef;
4724+
const isBaseRefSameAsRef = baseRef === ref;
47214725
// If base is commit SHA we will do comparison against the referenced commit
47224726
// Or if base references same branch it was pushed to, we will do comparison against the previously pushed commit
4723-
if (isBaseRefSha || isBaseSameAsPush) {
4727+
if (isBaseRefSha || isBaseRefSameAsRef) {
47244728
if (!isBaseRefSha && !beforeSha) {
47254729
core.warning(`'before' field is missing in event payload - changes will be detected from last commit`);
47264730
return await git.getChangesInLastCommit();
@@ -4731,7 +4735,7 @@ async function getChangedFilesFromGit(base, initialFetchDepth) {
47314735
if (baseSha === git.NULL_SHA) {
47324736
if (defaultRef && baseRef !== defaultRef) {
47334737
core.info(`First push of a branch detected - changes will be detected against the default branch ${defaultRef}`);
4734-
return await git.getChangesSinceMergeBase(defaultRef, initialFetchDepth);
4738+
return await git.getChangesSinceMergeBase(defaultRef, ref, initialFetchDepth);
47354739
}
47364740
else {
47374741
core.info('Initial push detected - all files will be listed as added');
@@ -4743,7 +4747,7 @@ async function getChangedFilesFromGit(base, initialFetchDepth) {
47434747
}
47444748
// Changes introduced by current branch against the base branch
47454749
core.info(`Changes will be detected against the branch ${baseRef}`);
4746-
return await git.getChangesSinceMergeBase(baseRef, initialFetchDepth);
4750+
return await git.getChangesSinceMergeBase(baseRef, ref, initialFetchDepth);
47474751
}
47484752
// Uses github REST api to get list of files changed in PR
47494753
async function getChangedFilesFromApi(token, pullRequest) {

src/git.ts

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ export async function getChangesInLastCommit(): Promise<File[]> {
1818
return parseGitDiffOutput(output)
1919
}
2020

21-
export async function getChanges(ref: string): Promise<File[]> {
22-
if (!(await hasCommit(ref))) {
21+
export async function getChanges(baseRef: string): Promise<File[]> {
22+
if (!(await hasCommit(baseRef))) {
2323
// Fetch single commit
24-
core.startGroup(`Fetching ${ref} from origin`)
25-
await exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', ref])
24+
core.startGroup(`Fetching ${baseRef} from origin`)
25+
await exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', baseRef])
2626
core.endGroup()
2727
}
2828

2929
// Get differences between ref and HEAD
30-
core.startGroup(`Change detection ${ref}..HEAD`)
30+
core.startGroup(`Change detection ${baseRef}..HEAD`)
3131
let output = ''
3232
try {
3333
// Two dots '..' change detection - directly compares two versions
34-
output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}..HEAD`])).stdout
34+
output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}..HEAD`])).stdout
3535
} finally {
3636
fixStdOutNullTermination()
3737
core.endGroup()
@@ -54,50 +54,56 @@ export async function getChangesOnHead(): Promise<File[]> {
5454
return parseGitDiffOutput(output)
5555
}
5656

57-
export async function getChangesSinceMergeBase(ref: string, initialFetchDepth: number): Promise<File[]> {
58-
if (!(await hasCommit(ref))) {
59-
// Fetch and add base branch
60-
core.startGroup(`Fetching ${ref}`)
61-
try {
62-
await exec('git', ['fetch', `--depth=${initialFetchDepth}`, '--no-tags', 'origin', `${ref}:${ref}`])
63-
} finally {
64-
core.endGroup()
65-
}
66-
}
67-
57+
export async function getChangesSinceMergeBase(
58+
baseRef: string,
59+
ref: string,
60+
initialFetchDepth: number
61+
): Promise<File[]> {
6862
async function hasMergeBase(): Promise<boolean> {
69-
return (await exec('git', ['merge-base', ref, 'HEAD'], {ignoreReturnCode: true})).code === 0
63+
return (await exec('git', ['merge-base', baseRef, ref], {ignoreReturnCode: true})).code === 0
7064
}
7165

72-
async function countCommits(): Promise<number> {
73-
return (await getNumberOfCommits('HEAD')) + (await getNumberOfCommits(ref))
66+
let noMergeBase = false
67+
core.startGroup(`Searching for merge-base ${baseRef}...${ref}`)
68+
try {
69+
let init = true
70+
let lastCommitCount = await getCommitCount()
71+
let depth = Math.max(lastCommitCount * 2, initialFetchDepth)
72+
while (!(await hasMergeBase())) {
73+
if (init) {
74+
await exec('git', ['fetch', `--depth=${depth}`, 'origin', `${baseRef}:${baseRef}`, `${ref}`])
75+
init = false
76+
} else {
77+
await exec('git', ['fetch', `--deepen=${depth}`, 'origin', baseRef, ref])
78+
}
79+
const commitCount = await getCommitCount()
80+
if (commitCount === lastCommitCount) {
81+
core.info('No more commits were fetched')
82+
core.info('Last attempt will be to fetch full history')
83+
await exec('git', ['fetch', '--unshallow'])
84+
if (!(await hasMergeBase())) {
85+
noMergeBase = true
86+
}
87+
break
88+
}
89+
depth = Math.min(depth * 2, Number.MAX_SAFE_INTEGER)
90+
lastCommitCount = commitCount
91+
}
92+
} finally {
93+
core.endGroup()
7494
}
7595

76-
core.startGroup(`Searching for merge-base with ${ref}`)
77-
// Fetch more commits until merge-base is found
78-
if (!(await hasMergeBase())) {
79-
let deepen = initialFetchDepth
80-
let lastCommitsCount = await countCommits()
81-
do {
82-
await exec('git', ['fetch', `--deepen=${deepen}`, '--no-tags'])
83-
const count = await countCommits()
84-
if (count <= lastCommitsCount) {
85-
core.info('No merge base found - all files will be listed as added')
86-
core.endGroup()
87-
return await listAllFilesAsAdded()
88-
}
89-
lastCommitsCount = count
90-
deepen = Math.min(deepen * 2, Number.MAX_SAFE_INTEGER)
91-
} while (!(await hasMergeBase()))
96+
if (noMergeBase) {
97+
core.warning('No merge base found - all files will be listed as added')
98+
return await listAllFilesAsAdded()
9299
}
93-
core.endGroup()
94100

95101
// Get changes introduced on HEAD compared to ref
96-
core.startGroup(`Change detection ${ref}...HEAD`)
102+
core.startGroup(`Change detection ${baseRef}...${ref}`)
97103
let output = ''
98104
try {
99105
// Three dots '...' change detection - finds merge-base and compares against it
100-
output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${ref}...HEAD`])).stdout
106+
output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', `${baseRef}...${ref}`])).stdout
101107
} finally {
102108
fixStdOutNullTermination()
103109
core.endGroup()
@@ -150,7 +156,7 @@ export async function getCurrentRef(): Promise<string> {
150156
return describe.stdout.trim()
151157
}
152158

153-
return (await exec('git', ['rev-parse', 'HEAD'])).stdout.trim()
159+
return (await exec('git', ['rev-parse', HEAD])).stdout.trim()
154160
} finally {
155161
core.endGroup()
156162
}
@@ -181,8 +187,8 @@ async function hasCommit(ref: string): Promise<boolean> {
181187
}
182188
}
183189

184-
async function getNumberOfCommits(ref: string): Promise<number> {
185-
const output = (await exec('git', ['rev-list', `--count`, ref])).stdout
190+
async function getCommitCount(): Promise<number> {
191+
const output = (await exec('git', ['rev-list', '--count', '--all'])).stdout
186192
const count = parseInt(output)
187193
return isNaN(count) ? 0 : count
188194
}

src/main.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async function run(): Promise<void> {
4040
}
4141

4242
function isPathInput(text: string): boolean {
43-
return !text.includes('\n')
43+
return !(text.includes('\n') || text.includes(':'))
4444
}
4545

4646
function getConfigFileContent(configPath: string): string {
@@ -80,7 +80,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number):
8080
const beforeSha =
8181
github.context.eventName === 'push' ? (github.context.payload as Webhooks.WebhookPayloadPush).before : null
8282

83-
const pushRef =
83+
const ref =
8484
git.getShortName(github.context.ref) ||
8585
(core.warning(`'ref' field is missing in event payload - using current branch, tag or commit SHA`),
8686
await git.getCurrentRef())
@@ -93,11 +93,11 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number):
9393
}
9494

9595
const isBaseRefSha = git.isGitSha(baseRef)
96-
const isBaseSameAsPush = baseRef === pushRef
96+
const isBaseRefSameAsRef = baseRef === ref
9797

9898
// If base is commit SHA we will do comparison against the referenced commit
9999
// Or if base references same branch it was pushed to, we will do comparison against the previously pushed commit
100-
if (isBaseRefSha || isBaseSameAsPush) {
100+
if (isBaseRefSha || isBaseRefSameAsRef) {
101101
if (!isBaseRefSha && !beforeSha) {
102102
core.warning(`'before' field is missing in event payload - changes will be detected from last commit`)
103103
return await git.getChangesInLastCommit()
@@ -109,7 +109,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number):
109109
if (baseSha === git.NULL_SHA) {
110110
if (defaultRef && baseRef !== defaultRef) {
111111
core.info(`First push of a branch detected - changes will be detected against the default branch ${defaultRef}`)
112-
return await git.getChangesSinceMergeBase(defaultRef, initialFetchDepth)
112+
return await git.getChangesSinceMergeBase(defaultRef, ref, initialFetchDepth)
113113
} else {
114114
core.info('Initial push detected - all files will be listed as added')
115115
return await git.listAllFilesAsAdded()
@@ -122,7 +122,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number):
122122

123123
// Changes introduced by current branch against the base branch
124124
core.info(`Changes will be detected against the branch ${baseRef}`)
125-
return await git.getChangesSinceMergeBase(baseRef, initialFetchDepth)
125+
return await git.getChangesSinceMergeBase(baseRef, ref, initialFetchDepth)
126126
}
127127

128128
// Uses github REST api to get list of files changed in PR

0 commit comments

Comments
 (0)