Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sparse checkout #1317

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ jobs:
shell: bash
run: __test__/verify-side-by-side.sh

# Sparse checkout
- name: Sparse checkout
uses: ./
with:
sparse-checkout: |
__test__
.github
dist
path: sparse-checkout

- name: Verify sparse checkout basic
run: __test__/verify-sparse-checkout-basic.sh
- name: Verify sparse checkout example
run: __test__/verify-sparse-checkout.sh

# LFS
- name: Checkout LFS
uses: ./
Expand Down Expand Up @@ -205,7 +220,7 @@ jobs:
path: basic
- name: Verify basic
run: __test__/verify-basic.sh --archive

test-git-container:
runs-on: ubuntu-latest
container: bitnami/git:latest
Expand Down
43 changes: 34 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# Default: true
clean: ''

# Do a sparse checkout on given patterns. Each pattern should be sepparated with
# new lines
# Default: null
sparse-checkout: ''

# Number of commits to fetch. 0 indicates all history for all branches and tags.
# Default: 1
fetch-depth: ''
Expand Down Expand Up @@ -106,15 +111,35 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl

# Scenarios

- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
- [Checkout a different branch](#Checkout-a-different-branch)
- [Checkout HEAD^](#Checkout-HEAD)
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
- [Checkout multiple repos (nested)](#Checkout-multiple-repos-nested)
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
- [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
- [Fetch only the root files](#fetch-only-the-root-files)
- [Fetch only the root files and `.github` and `src` folder](#fetch-only-the-root-files-and-github-and-src-folder)
- [Fetch all history for all tags and branches](#fetch-all-history-for-all-tags-and-branches)
- [Checkout a different branch](#checkout-a-different-branch)
- [Checkout HEAD^](#checkout-head)
- [Checkout multiple repos (side by side)](#checkout-multiple-repos-side-by-side)
- [Checkout multiple repos (nested)](#checkout-multiple-repos-nested)
- [Checkout multiple repos (private)](#checkout-multiple-repos-private)
- [Checkout pull request HEAD commit instead of merge commit](#checkout-pull-request-head-commit-instead-of-merge-commit)
- [Checkout pull request on closed event](#checkout-pull-request-on-closed-event)
- [Push a commit using the built-in token](#push-a-commit-using-the-built-in-token)

## Fetch only the root files

```yaml
- uses: actions/checkout@v3
with:
sparse-checkout: .
```

## Fetch only the root files and `.github` and `src` folder

```yaml
- uses: actions/checkout@v3
with:
sparse-checkout: |
.github
src
```

## Fetch all history for all tags and branches

Expand Down
2 changes: 2 additions & 0 deletions __test__/git-auth-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ async function setup(testName: string): Promise<void> {
branchDelete: jest.fn(),
branchExists: jest.fn(),
branchList: jest.fn(),
sparseCheckout: jest.fn(),
checkout: jest.fn(),
checkoutDetach: jest.fn(),
config: jest.fn(
Expand Down Expand Up @@ -800,6 +801,7 @@ async function setup(testName: string): Promise<void> {
authToken: 'some auth token',
clean: true,
commit: '',
sparseCheckout: [],
fetchDepth: 1,
lfs: false,
submodules: false,
Expand Down
1 change: 1 addition & 0 deletions __test__/git-directory-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ async function setup(testName: string): Promise<void> {
branchList: jest.fn(async () => {
return []
}),
sparseCheckout: jest.fn(),
checkout: jest.fn(),
checkoutDetach: jest.fn(),
config: jest.fn(),
Expand Down
1 change: 1 addition & 0 deletions __test__/input-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe('input-helper tests', () => {
expect(settings.clean).toBe(true)
expect(settings.commit).toBeTruthy()
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
expect(settings.sparseCheckout).toBe(undefined)
expect(settings.fetchDepth).toBe(1)
expect(settings.lfs).toBe(false)
expect(settings.ref).toBe('refs/heads/some-ref')
Expand Down
32 changes: 32 additions & 0 deletions __test__/verify-sparse-checkout-basic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

# Verify .git folder
if [ ! -d "./sparse-checkout/.git" ]; then
echo "Expected ./sparse-checkout/.git folder to exist"
exit 1
fi

# Verify sparse-checkout basic
cd sparse-checkout

SPARSE=$(git sparse-checkout list)

if [ "$?" != "0" ]; then
echo "Failed to validate sparse-checkout"
exit 1
fi

# Check that sparse-checkout list is not empty
if [ -z "$SPARSE" ]; then
echo "Expected sparse-checkout list to not be empty"
exit 1
fi

# Check that all folders from sparse-checkout exists
for pattern in $(git sparse-checkout list)
do
if [ ! -d "$pattern" ]; then
echo "Expected directory '$pattern' to exist"
exit 1
fi
done
41 changes: 41 additions & 0 deletions __test__/verify-sparse-checkout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

# Verify .git folder
if [ ! -d "./sparse-checkout/.git" ]; then
echo "Expected ./sparse-checkout/.git folder to exist"
exit 1
fi

# Verify sparse-checkout
cd sparse-checkout

checkSparse () {
if [ ! -d "./$1" ]; then
echo "Expected directory '$1' to exist"
exit 1
fi

for file in $(git ls-tree -r --name-only HEAD $1)
do
if [ ! -f "$file" ]; then
echo "Expected file '$file' to exist"
exit 1
fi
done
}

# Check that all folders and its childrens has been fetched correctly
checkSparse __test__
checkSparse .github
checkSparse dist

# Check that only sparse-checkout folders has been fetched
for pattern in $(git ls-tree --name-only HEAD)
do
if [ -d "$pattern" ]; then
if [[ "$pattern" != "__test__" && "$pattern" != ".github" && "$pattern" != "dist" ]]; then
echo "Expected directory '$pattern' to not exist"
exit 1
fi
fi
done
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ inputs:
clean:
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true
sparse-checkout:
description: >
Do a sparse checkout on given patterns.
Each pattern should be sepparated with new lines
default: null
fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
default: 1
Expand Down
40 changes: 32 additions & 8 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@ class GitCommandManager {
return result;
});
}
sparseCheckout(sparseCheckout) {
return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['sparse-checkout', 'set', ...sparseCheckout]);
});
}
checkout(ref, startPoint) {
return __awaiter(this, void 0, void 0, function* () {
const args = ['checkout', '--progress', '--force'];
Expand Down Expand Up @@ -615,15 +620,18 @@ class GitCommandManager {
return output.exitCode === 0;
});
}
fetch(refSpec, fetchDepth) {
fetch(refSpec, options) {
return __awaiter(this, void 0, void 0, function* () {
const args = ['-c', 'protocol.version=2', 'fetch'];
if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
args.push('--no-tags');
}
args.push('--prune', '--progress', '--no-recurse-submodules');
if (fetchDepth && fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`);
if (options.filter) {
args.push(`--filter=${options.filter}`);
}
if (options.fetchDepth && options.fetchDepth > 0) {
args.push(`--depth=${options.fetchDepth}`);
}
else if (fshelper.fileExistsSync(path.join(this.workingDirectory, '.git', 'shallow'))) {
args.push('--unshallow');
Expand Down Expand Up @@ -696,8 +704,8 @@ class GitCommandManager {
}
log1(format) {
return __awaiter(this, void 0, void 0, function* () {
var args = format ? ['log', '-1', format] : ['log', '-1'];
var silent = format ? false : true;
const args = format ? ['log', '-1', format] : ['log', '-1'];
const silent = format ? false : true;
const output = yield this.execGit(args, false, silent);
return output.stdout;
});
Expand Down Expand Up @@ -1210,20 +1218,24 @@ function getSource(settings) {
}
// Fetch
core.startGroup('Fetching the repository');
const fetchOptions = {};
if (settings.sparseCheckout)
fetchOptions.filter = 'blob:none';
if (settings.fetchDepth <= 0) {
// Fetch all branches and tags
let refSpec = refHelper.getRefSpecForAllHistory(settings.ref, settings.commit);
yield git.fetch(refSpec);
yield git.fetch(refSpec, fetchOptions);
// When all history is fetched, the ref we're interested in may have moved to a different
// commit (push or force push). If so, fetch again with a targeted refspec.
if (!(yield refHelper.testRef(git, settings.ref, settings.commit))) {
refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
yield git.fetch(refSpec);
yield git.fetch(refSpec, fetchOptions);
}
}
else {
fetchOptions.fetchDepth = settings.fetchDepth;
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
yield git.fetch(refSpec, settings.fetchDepth);
yield git.fetch(refSpec, fetchOptions);
}
core.endGroup();
// Checkout info
Expand All @@ -1238,6 +1250,12 @@ function getSource(settings) {
yield git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref);
core.endGroup();
}
// Sparse checkout
if (settings.sparseCheckout) {
core.startGroup('Setting up sparse checkout');
yield git.sparseCheckout(settings.sparseCheckout);
core.endGroup();
}
// Checkout
core.startGroup('Checking out the ref');
yield git.checkout(checkoutInfo.ref, checkoutInfo.startPoint);
Expand Down Expand Up @@ -1673,6 +1691,12 @@ function getInputs() {
// Clean
result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE';
core.debug(`clean = ${result.clean}`);
// Sparse checkout
const sparseCheckout = core.getMultilineInput('sparse-checkout');
if (sparseCheckout.length) {
result.sparseCheckout = sparseCheckout;
core.debug(`sparse checkout = ${result.sparseCheckout}`);
}
// Fetch depth
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'));
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {
Expand Down
31 changes: 25 additions & 6 deletions src/git-command-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface IGitCommandManager {
branchDelete(remote: boolean, branch: string): Promise<void>
branchExists(remote: boolean, pattern: string): Promise<boolean>
branchList(remote: boolean): Promise<string[]>
sparseCheckout(sparseCheckout: string[]): Promise<void>
checkout(ref: string, startPoint: string): Promise<void>
checkoutDetach(): Promise<void>
config(
Expand All @@ -25,7 +26,13 @@ export interface IGitCommandManager {
add?: boolean
): Promise<void>
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
fetch(refSpec: string[], fetchDepth?: number): Promise<void>
fetch(
refSpec: string[],
options: {
filter?: string
fetchDepth?: number
}
): Promise<void>
getDefaultBranch(repositoryUrl: string): Promise<string>
getWorkingDirectory(): string
init(): Promise<void>
Expand Down Expand Up @@ -154,6 +161,10 @@ class GitCommandManager {
return result
}

async sparseCheckout(sparseCheckout: string[]): Promise<void> {
await this.execGit(['sparse-checkout', 'set', ...sparseCheckout])
}

async checkout(ref: string, startPoint: string): Promise<void> {
const args = ['checkout', '--progress', '--force']
if (startPoint) {
Expand Down Expand Up @@ -202,15 +213,23 @@ class GitCommandManager {
return output.exitCode === 0
}

async fetch(refSpec: string[], fetchDepth?: number): Promise<void> {
async fetch(
refSpec: string[],
options: {filter?: string; fetchDepth?: number}
): Promise<void> {
const args = ['-c', 'protocol.version=2', 'fetch']
if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
args.push('--no-tags')
}

args.push('--prune', '--progress', '--no-recurse-submodules')
if (fetchDepth && fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`)

if (options.filter) {
args.push(`--filter=${options.filter}`)
}

if (options.fetchDepth && options.fetchDepth > 0) {
args.push(`--depth=${options.fetchDepth}`)
} else if (
fshelper.fileExistsSync(
path.join(this.workingDirectory, '.git', 'shallow')
Expand Down Expand Up @@ -289,8 +308,8 @@ class GitCommandManager {
}

async log1(format?: string): Promise<string> {
var args = format ? ['log', '-1', format] : ['log', '-1']
var silent = format ? false : true
const args = format ? ['log', '-1', format] : ['log', '-1']
const silent = format ? false : true
const output = await this.execGit(args, false, silent)
return output.stdout
}
Expand Down
Loading