Skip to content

Commit 8c354c5

Browse files
authored
fix: throw when detecting a shallow clone
* fix: warn about shallow clones * docs: help with shallow clone troubleshooting * closes #7 * closes #12 * test: simplify repo setup and teardown
1 parent 4659146 commit 8c354c5

File tree

4 files changed

+142
-37
lines changed

4 files changed

+142
-37
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
},
6969
"repository": {
7070
"type": "git",
71-
"url": "git+https://github.com/marionebl/conventional-changelog-lint.git"
71+
"url": "https://github.com/marionebl/conventional-changelog-lint.git"
7272
},
7373
"bugs": {
7474
"url": "https://github.com/marionebl/conventional-changelog-lint/issues"

readme.md

+75-23
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ resolves `extends` configurations.
2929

3030
```shell
3131
❯ conventional-changelog-lint --help
32-
conventional-changelog-lint@0.1.0 - Lint commit messages against a conventional-changelog preset and ruleset
32+
conventional-changelog-lint - Lint commit messages against a conventional-changelog preset and ruleset
3333

3434
[input] reads from stdin if --edit, --from, --to are omitted
3535
--color,-c toggle formatted output, defaults to: true
@@ -42,6 +42,41 @@ resolves `extends` configurations.
4242

4343
```
4444
45+
### Recipes
46+
47+
#### git hook
48+
As a `commitmsg` git-hook with ["husky"](https://git.io/JDwyQg)
49+
50+
```json
51+
{
52+
"scripts": {
53+
"commitmsg": "conventional-changelog-lint -e"
54+
}
55+
}
56+
```
57+
58+
59+
#### Last commit
60+
As part of `npm test`
61+
62+
```json
63+
{
64+
"scripts": {
65+
"test": "conventional-changelog-lint --from=HEAD~1"
66+
}
67+
}
68+
```
69+
70+
#### Travis
71+
72+
```yml
73+
# Force full git checkout
74+
before_install: git fetch --unshallow
75+
76+
# Lint all commits not in the target branch
77+
before_script: conventional-changelog-lint --from=$TRAVIS_BRANCH to=$TRAVIS_PULL_REQUEST_BRANCH
78+
```
79+
4580
### API
4681
4782
The programming interface does not read configuration by default,
@@ -77,28 +112,6 @@ const report = lint(
77112
);
78113
```
79114
80-
### Recipes
81-
82-
* As a `commitmsg` git-hook with ["husky"](https://git.io/JDwyQg)
83-
84-
```json
85-
{
86-
"scripts": {
87-
"commitmsg": "conventional-changelog-lint -e"
88-
}
89-
}
90-
```
91-
92-
* As part of `npm test`
93-
94-
```json
95-
{
96-
"scripts": {
97-
"test": "conventional-changelog-lint --from=HEAD~1"
98-
}
99-
}
100-
```
101-
102115
## Configuration
103116
104117
`conventional-changelog-lint` is configured via
@@ -186,6 +199,45 @@ wildcards: {
186199
}
187200
```
188201
202+
## Shallow clones
203+
204+
### TL;DR
205+
206+
Perform `git fetch --shallow` before linting.
207+
208+
Most likely you are reading this because you where presented with an error message:
209+
210+
```
211+
'Could not get git history from shallow clone.
212+
Use git fetch --shallow before linting.
213+
Original issue: https://git.io/vyKMq\n Refer to https://git.io/vyKMv for details.'
214+
```
215+
216+
### Explanation
217+
218+
git supports checking out `shallow` clones of a repository to save bandwith in times.
219+
These limited copies do not contain a full git history. This makes `conventional-changelog-lint`
220+
fail, especially when running on large commit ranges.
221+
To ensure linting works every time you should convert a shallow git repo to a complete one.
222+
Use `git fetch --shallow` to do so.
223+
224+
### Travis
225+
226+
Ensure full git checkouts on TravisCI, add to `.travis.yml`:
227+
228+
```yml
229+
before_install:
230+
- git fetch --unshallow
231+
```
232+
233+
### Appveyor
234+
235+
Ensure full git checkouts on AppVeyor, add to `appveyor.yml`:
236+
237+
```yml
238+
shallow_clone: false
239+
```
240+
189241
## Supported Node.js versions
190242
191243
conventional-changelog-lint supports the active Node.js [LTS](https://github.com/nodejs/LTS#lts-schedule) version and higher: `>= 4`

source/library/get-messages.js

+19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import {join} from 'path';
2+
import exists from 'path-exists';
23
import gitRawCommits from 'git-raw-commits';
34
import gitToplevel from 'git-toplevel';
45
import {readFile} from 'mz/fs';
56

67
export default getCommitMessages;
78

9+
const SHALLOW_MESSAGE = [
10+
'Could not get git history from shallow clone.',
11+
'Use git fetch --shallow before linting.',
12+
'Original issue: https://git.io/vyKMq\n Refer to https://git.io/vyKMv for details.'
13+
].join('\n');
14+
815
// Get commit messages
916
// Object => Promise<Array<String>>
1017
async function getCommitMessages(settings) {
@@ -14,6 +21,10 @@ async function getCommitMessages(settings) {
1421
return getEditCommit();
1522
}
1623

24+
if (await isShallow()) {
25+
throw new Error(SHALLOW_MESSAGE);
26+
}
27+
1728
return await getHistoryCommits({from, to});
1829
}
1930

@@ -31,6 +42,14 @@ function getHistoryCommits(options) {
3142
});
3243
}
3344

45+
// Check if the current repository is shallow
46+
// () => Promise<Boolean>
47+
async function isShallow() {
48+
const top = await gitToplevel();
49+
const shallow = join(top, '.git/shallow');
50+
return await exists(shallow);
51+
}
52+
3453
// Get recently edited commit message
3554
// () => Promise<Array<String>>
3655
async function getEditCommit() {

test/integration/get-messages.js

+47-13
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,36 @@ import rimraf from 'rimraf';
1111
import expect from 'unexpected';
1212

1313
import getMessages from '../../source/library/get-messages';
14+
import pkg from '../../package';
1415

1516
const rm = denodeify(rimraf);
1617

17-
test.serial('get edit commit message from git root', async () => {
18-
const repo = await initRepository();
18+
test.beforeEach(async t => {
19+
t.context.repos = [await initRepository()];
20+
});
21+
22+
test.afterEach.always(async t => {
23+
try {
24+
await Promise.all(t.context.repos.map(async repo => cleanRepository(repo)));
25+
t.context.repos = [];
26+
} catch (err) {
27+
console.log({err});
28+
}
29+
});
30+
31+
test.serial('get edit commit message from git root', async t => {
32+
const [repo] = t.context.repos;
1933

2034
await writeFile('alpha.txt', 'alpha');
2135
await execa('git', ['add', '.']);
2236
await execa('git', ['commit', '-m', 'alpha']);
23-
2437
const expected = ['alpha\n\n'];
2538
const actual = await getMessages({edit: true});
2639
expect(actual, 'to equal', expected);
27-
28-
await cleanRepository(repo);
2940
});
3041

31-
test.serial('get history commit messages', async () => {
32-
const repo = await initRepository();
42+
test.serial('get history commit messages', async t => {
43+
const [repo] = t.context.repos;
3344

3445
await writeFile('alpha.txt', 'alpha');
3546
await execa('git', ['add', 'alpha.txt']);
@@ -40,12 +51,10 @@ test.serial('get history commit messages', async () => {
4051
const expected = ['remove alpha\n\n', 'alpha\n\n'];
4152
const actual = await getMessages({});
4253
expect(actual, 'to equal', expected);
43-
44-
await cleanRepository(repo);
4554
});
4655

47-
test.serial('get edit commit message from git subdirectory', async () => {
48-
const repo = await initRepository();
56+
test.serial('get edit commit message from git subdirectory', async t => {
57+
const [repo] = t.context.repos;
4958

5059
await mkdir('beta');
5160
await writeFile('beta/beta.txt', 'beta');
@@ -56,8 +65,20 @@ test.serial('get edit commit message from git subdirectory', async () => {
5665
const expected = ['beta\n\n'];
5766
const actual = await getMessages({edit: true});
5867
expect(actual, 'to equal', expected);
68+
});
69+
70+
test.serial('get history commit messages from shallow clone', async t => {
71+
const [repo] = t.context.repos;
72+
73+
await writeFile('alpha.txt', 'alpha');
74+
await execa('git', ['add', 'alpha.txt']);
75+
await execa('git', ['commit', '-m', 'alpha']);
76+
77+
const clone = await cloneRepository(pkg.repository.url, repo, '--depth', '1');
78+
t.context.repos = [...t.context.repos, clone];
5979

60-
await cleanRepository(repo);
80+
const err = await t.throws(getMessages({from: 'master'}));
81+
expect(err.message, 'to contain', 'Could not get git history from shallow clone');
6182
});
6283

6384
async function initRepository() {
@@ -74,8 +95,21 @@ async function initRepository() {
7495
return {directory, previous};
7596
}
7697

98+
async function cloneRepository(source, context, ...args) {
99+
const directory = join(tmpdir(), rand());
100+
await execa('git', ['clone', ...args, source, directory]);
101+
process.chdir(directory);
102+
103+
await execa('git', ['config', 'user.email', '[email protected]']);
104+
await execa('git', ['config', 'user.name', 'ava']);
105+
106+
return {directory, previous: context.previous};
107+
}
108+
77109
async function cleanRepository(repo) {
78-
process.chdir(repo.previous);
110+
if (repo.previous && repo.previous !== process.cwd()) {
111+
process.chdir(repo.previous);
112+
}
79113

80114
if (await exists(repo.directory)) {
81115
await rm(repo.directory);

0 commit comments

Comments
 (0)