Skip to content

Commit 0d9db6b

Browse files
committed
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>
1 parent 9f59c81 commit 0d9db6b

12 files changed

+140
-2
lines changed

.github/workflows/test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ jobs:
8585
- name: Verify sparse checkout
8686
run: __test__/verify-sparse-checkout.sh
8787

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+
88102
# LFS
89103
- name: Checkout LFS
90104
uses: ./

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
7979
# Default: null
8080
sparse-checkout: ''
8181

82+
# Specifies whether to use cone-mode when doing a sparse checkout.
83+
# Default: true
84+
sparse-checkout-cone-mode: ''
85+
8286
# Number of commits to fetch. 0 indicates all history for all branches and tags.
8387
# Default: 1
8488
fetch-depth: ''
@@ -113,6 +117,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
113117
114118
- [Fetch only the root files](#Fetch-only-the-root-files)
115119
- [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)
116121
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
117122
- [Checkout a different branch](#Checkout-a-different-branch)
118123
- [Checkout HEAD^](#Checkout-HEAD)
@@ -141,6 +146,16 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
141146
src
142147
```
143148

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+
144159
## Fetch all history for all tags and branches
145160

146161
```yaml

__test__/git-auth-helper.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ async function setup(testName: string): Promise<void> {
728728
branchExists: jest.fn(),
729729
branchList: jest.fn(),
730730
sparseCheckout: jest.fn(),
731+
sparseCheckoutNonConeMode: jest.fn(),
731732
checkout: jest.fn(),
732733
checkoutDetach: jest.fn(),
733734
config: jest.fn(
@@ -802,6 +803,7 @@ async function setup(testName: string): Promise<void> {
802803
clean: true,
803804
commit: '',
804805
sparseCheckout: [],
806+
sparseCheckoutConeMode: true,
805807
fetchDepth: 1,
806808
lfs: false,
807809
submodules: false,

__test__/git-directory-helper.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ async function setup(testName: string): Promise<void> {
463463
return []
464464
}),
465465
sparseCheckout: jest.fn(),
466+
sparseCheckoutNonConeMode: jest.fn(),
466467
checkout: jest.fn(),
467468
checkoutDetach: jest.fn(),
468469
config: jest.fn(),

__test__/input-helper.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ describe('input-helper tests', () => {
8080
expect(settings.commit).toBeTruthy()
8181
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
8282
expect(settings.sparseCheckout).toBe(undefined)
83+
expect(settings.sparseCheckoutConeMode).toBe(true)
8384
expect(settings.fetchDepth).toBe(1)
8485
expect(settings.lfs).toBe(false)
8586
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

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ inputs:
5858
Do a sparse checkout on given patterns.
5959
Each pattern should be separated with new lines
6060
default: null
61+
sparse-checkout-cone-mode:
62+
description: >
63+
Specifies whether to use cone-mode when doing a sparse checkout.
64+
default: true
6165
fetch-depth:
6266
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
6367
default: 1

dist/index.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
470470
exports.createCommandManager = exports.MinimumGitVersion = void 0;
471471
const core = __importStar(__nccwpck_require__(2186));
472472
const exec = __importStar(__nccwpck_require__(1514));
473+
const fs = __importStar(__nccwpck_require__(7147));
473474
const fshelper = __importStar(__nccwpck_require__(7219));
474475
const io = __importStar(__nccwpck_require__(7436));
475476
const path = __importStar(__nccwpck_require__(1017));
@@ -579,6 +580,18 @@ class GitCommandManager {
579580
yield this.execGit(['sparse-checkout', 'set', ...sparseCheckout]);
580581
});
581582
}
583+
sparseCheckoutNonConeMode(sparseCheckout) {
584+
return __awaiter(this, void 0, void 0, function* () {
585+
yield this.execGit(['config', 'core.sparseCheckout', 'true']);
586+
const output = yield this.execGit([
587+
'rev-parse',
588+
'--git-path',
589+
'info/sparse-checkout'
590+
]);
591+
const sparseCheckoutPath = output.stdout.trimRight();
592+
yield fs.promises.appendFile(sparseCheckoutPath, `\n${sparseCheckout.join('\n')}\n`);
593+
});
594+
}
582595
checkout(ref, startPoint) {
583596
return __awaiter(this, void 0, void 0, function* () {
584597
const args = ['checkout', '--progress', '--force'];
@@ -1253,7 +1266,12 @@ function getSource(settings) {
12531266
// Sparse checkout
12541267
if (settings.sparseCheckout) {
12551268
core.startGroup('Setting up sparse checkout');
1256-
yield git.sparseCheckout(settings.sparseCheckout);
1269+
if (settings.sparseCheckoutConeMode) {
1270+
yield git.sparseCheckout(settings.sparseCheckout);
1271+
}
1272+
else {
1273+
yield git.sparseCheckoutNonConeMode(settings.sparseCheckout);
1274+
}
12571275
core.endGroup();
12581276
}
12591277
// Checkout
@@ -1697,6 +1715,9 @@ function getInputs() {
16971715
result.sparseCheckout = sparseCheckout;
16981716
core.debug(`sparse checkout = ${result.sparseCheckout}`);
16991717
}
1718+
result.sparseCheckoutConeMode =
1719+
(core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() !==
1720+
'FALSE';
17001721
// Fetch depth
17011722
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'));
17021723
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {

src/git-command-manager.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as core from '@actions/core'
22
import * as exec from '@actions/exec'
3+
import * as fs from 'fs'
34
import * as fshelper from './fs-helper'
45
import * as io from '@actions/io'
56
import * as path from 'path'
@@ -17,6 +18,7 @@ export interface IGitCommandManager {
1718
branchExists(remote: boolean, pattern: string): Promise<boolean>
1819
branchList(remote: boolean): Promise<string[]>
1920
sparseCheckout(sparseCheckout: string[]): Promise<void>
21+
sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void>
2022
checkout(ref: string, startPoint: string): Promise<void>
2123
checkoutDetach(): Promise<void>
2224
config(
@@ -165,6 +167,20 @@ class GitCommandManager {
165167
await this.execGit(['sparse-checkout', 'set', ...sparseCheckout])
166168
}
167169

170+
async sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void> {
171+
await this.execGit(['config', 'core.sparseCheckout', 'true'])
172+
const output = await this.execGit([
173+
'rev-parse',
174+
'--git-path',
175+
'info/sparse-checkout'
176+
])
177+
const sparseCheckoutPath = output.stdout.trimRight()
178+
await fs.promises.appendFile(
179+
sparseCheckoutPath,
180+
`\n${sparseCheckout.join('\n')}\n`
181+
)
182+
}
183+
168184
async checkout(ref: string, startPoint: string): Promise<void> {
169185
const args = ['checkout', '--progress', '--force']
170186
if (startPoint) {

src/git-source-provider.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
197197
// Sparse checkout
198198
if (settings.sparseCheckout) {
199199
core.startGroup('Setting up sparse checkout')
200-
await git.sparseCheckout(settings.sparseCheckout)
200+
if (settings.sparseCheckoutConeMode) {
201+
await git.sparseCheckout(settings.sparseCheckout)
202+
} else {
203+
await git.sparseCheckoutNonConeMode(settings.sparseCheckout)
204+
}
201205
core.endGroup()
202206
}
203207

0 commit comments

Comments
 (0)