Skip to content

Commit d106d46

Browse files
dschodfdez
andauthored
Add support for sparse checkouts (#1369)
* Add support for sparse checkouts * sparse-checkout: optionally turn off cone mode While it _is_ true that cone mode is the default nowadays (mainly for performance reasons: code mode is much faster than non-cone mode), there _are_ legitimate use cases where non-cone mode is really useful. Let's add a flag to optionally disable cone mode. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> * Verify minimum Git version for sparse checkout The `git sparse-checkout` command is available only since Git version v2.25.0. The `actions/checkout` Action actually supports older Git versions than that; As of time of writing, the minimum version is v2.18.0. Instead of raising this minimum version even for users who do not require a sparse checkout, only check for this minimum version specifically when a sparse checkout was asked for. Suggested-by: Tingluo Huang <tingluohuang@github.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> * Support sparse checkout/LFS better Instead of fetching all the LFS objects present in the current revision in a sparse checkout, whether they are needed inside the sparse cone or not, let's instead only pull the ones that are actually needed. To do that, let's avoid running that preemptive `git lfs fetch` call in case of a sparse checkout. An alternative that was considered during the development of this patch (and ultimately rejected) was to use `git lfs pull --include <path>...`, but it turned out to be too inflexible because it requires exact paths, not the patterns that are available via the sparse checkout definition, and that risks running into command-line length limitations. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --------- Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Co-authored-by: Daniel <daniel.fernandez@feverup.com>
1 parent f095bcc commit d106d46

14 files changed

+395
-31
lines changed

.github/workflows/test.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,33 @@ jobs:
7272
shell: bash
7373
run: __test__/verify-side-by-side.sh
7474

75+
# Sparse checkout
76+
- name: Sparse checkout
77+
uses: ./
78+
with:
79+
sparse-checkout: |
80+
__test__
81+
.github
82+
dist
83+
path: sparse-checkout
84+
85+
- name: Verify sparse checkout
86+
run: __test__/verify-sparse-checkout.sh
87+
88+
# Sparse checkout (non-cone mode)
89+
- name: Sparse checkout (non-cone mode)
90+
uses: ./
91+
with:
92+
sparse-checkout: |
93+
/__test__/
94+
/.github/
95+
/dist/
96+
sparse-checkout-cone-mode: false
97+
path: sparse-checkout-non-cone-mode
98+
99+
- name: Verify sparse checkout (non-cone mode)
100+
run: __test__/verify-sparse-checkout-non-cone-mode.sh
101+
75102
# LFS
76103
- name: Checkout LFS
77104
uses: ./

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
7474
# Default: true
7575
clean: ''
7676

77+
# Do a sparse checkout on given patterns. Each pattern should be separated with
78+
# new lines
79+
# Default: null
80+
sparse-checkout: ''
81+
82+
# Specifies whether to use cone-mode when doing a sparse checkout.
83+
# Default: true
84+
sparse-checkout-cone-mode: ''
85+
7786
# Number of commits to fetch. 0 indicates all history for all branches and tags.
7887
# Default: 1
7988
fetch-depth: ''
@@ -106,6 +115,9 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
106115
107116
# Scenarios
108117
118+
- [Fetch only the root files](#Fetch-only-the-root-files)
119+
- [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder)
120+
- [Fetch only a single file](#Fetch-only-a-single-file)
109121
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
110122
- [Checkout a different branch](#Checkout-a-different-branch)
111123
- [Checkout HEAD^](#Checkout-HEAD)
@@ -116,6 +128,34 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
116128
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
117129
- [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
118130

131+
## Fetch only the root files
132+
133+
```yaml
134+
- uses: actions/checkout@v3
135+
with:
136+
sparse-checkout: .
137+
```
138+
139+
## Fetch only the root files and `.github` and `src` folder
140+
141+
```yaml
142+
- uses: actions/checkout@v3
143+
with:
144+
sparse-checkout: |
145+
.github
146+
src
147+
```
148+
149+
## Fetch only a single file
150+
151+
```yaml
152+
- uses: actions/checkout@v3
153+
with:
154+
sparse-checkout: |
155+
README.md
156+
sparse-checkout-cone-mode: false
157+
```
158+
119159
## Fetch all history for all tags and branches
120160

121161
```yaml

__test__/git-auth-helper.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,8 @@ async function setup(testName: string): Promise<void> {
727727
branchDelete: jest.fn(),
728728
branchExists: jest.fn(),
729729
branchList: jest.fn(),
730+
sparseCheckout: jest.fn(),
731+
sparseCheckoutNonConeMode: jest.fn(),
730732
checkout: jest.fn(),
731733
checkoutDetach: jest.fn(),
732734
config: jest.fn(
@@ -800,6 +802,8 @@ async function setup(testName: string): Promise<void> {
800802
authToken: 'some auth token',
801803
clean: true,
802804
commit: '',
805+
sparseCheckout: [],
806+
sparseCheckoutConeMode: true,
803807
fetchDepth: 1,
804808
lfs: false,
805809
submodules: false,

__test__/git-command-manager.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ describe('git-auth-helper tests', () => {
3939
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
4040
const workingDirectory = 'test'
4141
const lfs = false
42-
git = await commandManager.createCommandManager(workingDirectory, lfs)
42+
const doSparseCheckout = false
43+
git = await commandManager.createCommandManager(
44+
workingDirectory,
45+
lfs,
46+
doSparseCheckout
47+
)
4348

4449
let branches = await git.branchList(false)
4550

@@ -70,7 +75,12 @@ describe('git-auth-helper tests', () => {
7075
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
7176
const workingDirectory = 'test'
7277
const lfs = false
73-
git = await commandManager.createCommandManager(workingDirectory, lfs)
78+
const doSparseCheckout = false
79+
git = await commandManager.createCommandManager(
80+
workingDirectory,
81+
lfs,
82+
doSparseCheckout
83+
)
7484

7585
let branches = await git.branchList(false)
7686

__test__/git-directory-helper.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ async function setup(testName: string): Promise<void> {
462462
branchList: jest.fn(async () => {
463463
return []
464464
}),
465+
sparseCheckout: jest.fn(),
466+
sparseCheckoutNonConeMode: jest.fn(),
465467
checkout: jest.fn(),
466468
checkoutDetach: jest.fn(),
467469
config: jest.fn(),

__test__/input-helper.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ describe('input-helper tests', () => {
7979
expect(settings.clean).toBe(true)
8080
expect(settings.commit).toBeTruthy()
8181
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
82+
expect(settings.sparseCheckout).toBe(undefined)
83+
expect(settings.sparseCheckoutConeMode).toBe(true)
8284
expect(settings.fetchDepth).toBe(1)
8385
expect(settings.lfs).toBe(false)
8486
expect(settings.ref).toBe('refs/heads/some-ref')
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/sh
2+
3+
# Verify .git folder
4+
if [ ! -d "./sparse-checkout-non-cone-mode/.git" ]; then
5+
echo "Expected ./sparse-checkout-non-cone-mode/.git folder to exist"
6+
exit 1
7+
fi
8+
9+
# Verify sparse-checkout (non-cone-mode)
10+
cd sparse-checkout-non-cone-mode
11+
12+
ENABLED=$(git config --local --get-all core.sparseCheckout)
13+
14+
if [ "$?" != "0" ]; then
15+
echo "Failed to verify that sparse-checkout is enabled"
16+
exit 1
17+
fi
18+
19+
# Check that sparse-checkout is enabled
20+
if [ "$ENABLED" != "true" ]; then
21+
echo "Expected sparse-checkout to be enabled (is: $ENABLED)"
22+
exit 1
23+
fi
24+
25+
SPARSE_CHECKOUT_FILE=$(git rev-parse --git-path info/sparse-checkout)
26+
27+
if [ "$?" != "0" ]; then
28+
echo "Failed to validate sparse-checkout"
29+
exit 1
30+
fi
31+
32+
# Check that sparse-checkout list is not empty
33+
if [ ! -f "$SPARSE_CHECKOUT_FILE" ]; then
34+
echo "Expected sparse-checkout file to exist"
35+
exit 1
36+
fi
37+
38+
# Check that all folders from sparse-checkout exists
39+
for pattern in $(cat "$SPARSE_CHECKOUT_FILE")
40+
do
41+
if [ ! -d "${pattern#/}" ]; then
42+
echo "Expected directory '${pattern#/}' to exist"
43+
exit 1
44+
fi
45+
done
46+
47+
# Verify that the root directory is not checked out
48+
if [ -f README.md ]; then
49+
echo "Expected top-level files not to exist"
50+
exit 1
51+
fi

__test__/verify-sparse-checkout.sh

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/sh
2+
3+
# Verify .git folder
4+
if [ ! -d "./sparse-checkout/.git" ]; then
5+
echo "Expected ./sparse-checkout/.git folder to exist"
6+
exit 1
7+
fi
8+
9+
# Verify sparse-checkout
10+
cd sparse-checkout
11+
12+
SPARSE=$(git sparse-checkout list)
13+
14+
if [ "$?" != "0" ]; then
15+
echo "Failed to validate sparse-checkout"
16+
exit 1
17+
fi
18+
19+
# Check that sparse-checkout list is not empty
20+
if [ -z "$SPARSE" ]; then
21+
echo "Expected sparse-checkout list to not be empty"
22+
exit 1
23+
fi
24+
25+
# Check that all folders of the sparse checkout exist
26+
for pattern in $SPARSE
27+
do
28+
if [ ! -d "$pattern" ]; then
29+
echo "Expected directory '$pattern' to exist"
30+
exit 1
31+
fi
32+
done
33+
34+
checkSparse () {
35+
if [ ! -d "./$1" ]; then
36+
echo "Expected directory '$1' to exist"
37+
exit 1
38+
fi
39+
40+
for file in $(git ls-tree -r --name-only HEAD $1)
41+
do
42+
if [ ! -f "$file" ]; then
43+
echo "Expected file '$file' to exist"
44+
exit 1
45+
fi
46+
done
47+
}
48+
49+
# Check that all folders and their children have been checked out
50+
checkSparse __test__
51+
checkSparse .github
52+
checkSparse dist
53+
54+
# Check that only sparse-checkout folders have been checked out
55+
for pattern in $(git ls-tree --name-only HEAD)
56+
do
57+
if [ -d "$pattern" ]; then
58+
if [[ "$pattern" != "__test__" && "$pattern" != ".github" && "$pattern" != "dist" ]]; then
59+
echo "Expected directory '$pattern' to not exist"
60+
exit 1
61+
fi
62+
fi
63+
done

action.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ inputs:
5353
clean:
5454
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
5555
default: true
56+
sparse-checkout:
57+
description: >
58+
Do a sparse checkout on given patterns.
59+
Each pattern should be separated with new lines
60+
default: null
61+
sparse-checkout-cone-mode:
62+
description: >
63+
Specifies whether to use cone-mode when doing a sparse checkout.
64+
default: true
5665
fetch-depth:
5766
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
5867
default: 1

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy