Skip to content

Commit

Permalink
fix: throw when detecting a shallow clone
Browse files Browse the repository at this point in the history
* fix: warn about shallow clones

* docs: help with shallow clone troubleshooting

* closes #7
* closes #12

* test: simplify repo setup and teardown
  • Loading branch information
marionebl committed May 1, 2017
1 parent 4659146 commit 8c354c5
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 37 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/marionebl/conventional-changelog-lint.git"
"url": "https://github.com/marionebl/conventional-changelog-lint.git"
},
"bugs": {
"url": "https://github.com/marionebl/conventional-changelog-lint/issues"
Expand Down
98 changes: 75 additions & 23 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ resolves `extends` configurations.

```shell
❯ conventional-changelog-lint --help
conventional-changelog-lint@0.1.0 - Lint commit messages against a conventional-changelog preset and ruleset
conventional-changelog-lint - Lint commit messages against a conventional-changelog preset and ruleset

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

```
### Recipes
#### git hook
As a `commitmsg` git-hook with ["husky"](https://git.io/JDwyQg)
```json
{
"scripts": {
"commitmsg": "conventional-changelog-lint -e"
}
}
```
#### Last commit
As part of `npm test`
```json
{
"scripts": {
"test": "conventional-changelog-lint --from=HEAD~1"
}
}
```
#### Travis
```yml
# Force full git checkout
before_install: git fetch --unshallow

# Lint all commits not in the target branch
before_script: conventional-changelog-lint --from=$TRAVIS_BRANCH to=$TRAVIS_PULL_REQUEST_BRANCH
```
### API
The programming interface does not read configuration by default,
Expand Down Expand Up @@ -77,28 +112,6 @@ const report = lint(
);
```
### Recipes
* As a `commitmsg` git-hook with ["husky"](https://git.io/JDwyQg)
```json
{
"scripts": {
"commitmsg": "conventional-changelog-lint -e"
}
}
```
* As part of `npm test`
```json
{
"scripts": {
"test": "conventional-changelog-lint --from=HEAD~1"
}
}
```
## Configuration
`conventional-changelog-lint` is configured via
Expand Down Expand Up @@ -186,6 +199,45 @@ wildcards: {
}
```
## Shallow clones
### TL;DR
Perform `git fetch --shallow` before linting.
Most likely you are reading this because you where presented with an error message:
```
'Could not get git history from shallow clone.
Use git fetch --shallow before linting.
Original issue: https://git.io/vyKMq\n Refer to https://git.io/vyKMv for details.'
```
### Explanation
git supports checking out `shallow` clones of a repository to save bandwith in times.
These limited copies do not contain a full git history. This makes `conventional-changelog-lint`
fail, especially when running on large commit ranges.
To ensure linting works every time you should convert a shallow git repo to a complete one.
Use `git fetch --shallow` to do so.
### Travis
Ensure full git checkouts on TravisCI, add to `.travis.yml`:
```yml
before_install:
- git fetch --unshallow
```
### Appveyor
Ensure full git checkouts on AppVeyor, add to `appveyor.yml`:
```yml
shallow_clone: false
```
## Supported Node.js versions
conventional-changelog-lint supports the active Node.js [LTS](https://github.com/nodejs/LTS#lts-schedule) version and higher: `>= 4`
Expand Down
19 changes: 19 additions & 0 deletions source/library/get-messages.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {join} from 'path';
import exists from 'path-exists';
import gitRawCommits from 'git-raw-commits';
import gitToplevel from 'git-toplevel';
import {readFile} from 'mz/fs';

export default getCommitMessages;

const SHALLOW_MESSAGE = [
'Could not get git history from shallow clone.',
'Use git fetch --shallow before linting.',
'Original issue: https://git.io/vyKMq\n Refer to https://git.io/vyKMv for details.'
].join('\n');

// Get commit messages
// Object => Promise<Array<String>>
async function getCommitMessages(settings) {
Expand All @@ -14,6 +21,10 @@ async function getCommitMessages(settings) {
return getEditCommit();
}

if (await isShallow()) {
throw new Error(SHALLOW_MESSAGE);
}

return await getHistoryCommits({from, to});
}

Expand All @@ -31,6 +42,14 @@ function getHistoryCommits(options) {
});
}

// Check if the current repository is shallow
// () => Promise<Boolean>
async function isShallow() {
const top = await gitToplevel();
const shallow = join(top, '.git/shallow');
return await exists(shallow);
}

// Get recently edited commit message
// () => Promise<Array<String>>
async function getEditCommit() {
Expand Down
60 changes: 47 additions & 13 deletions test/integration/get-messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,36 @@ import rimraf from 'rimraf';
import expect from 'unexpected';

import getMessages from '../../source/library/get-messages';
import pkg from '../../package';

const rm = denodeify(rimraf);

test.serial('get edit commit message from git root', async () => {
const repo = await initRepository();
test.beforeEach(async t => {
t.context.repos = [await initRepository()];
});

test.afterEach.always(async t => {
try {
await Promise.all(t.context.repos.map(async repo => cleanRepository(repo)));
t.context.repos = [];
} catch (err) {
console.log({err});
}
});

test.serial('get edit commit message from git root', async t => {
const [repo] = t.context.repos;

await writeFile('alpha.txt', 'alpha');
await execa('git', ['add', '.']);
await execa('git', ['commit', '-m', 'alpha']);

const expected = ['alpha\n\n'];
const actual = await getMessages({edit: true});
expect(actual, 'to equal', expected);

await cleanRepository(repo);
});

test.serial('get history commit messages', async () => {
const repo = await initRepository();
test.serial('get history commit messages', async t => {
const [repo] = t.context.repos;

await writeFile('alpha.txt', 'alpha');
await execa('git', ['add', 'alpha.txt']);
Expand All @@ -40,12 +51,10 @@ test.serial('get history commit messages', async () => {
const expected = ['remove alpha\n\n', 'alpha\n\n'];
const actual = await getMessages({});
expect(actual, 'to equal', expected);

await cleanRepository(repo);
});

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

await mkdir('beta');
await writeFile('beta/beta.txt', 'beta');
Expand All @@ -56,8 +65,20 @@ test.serial('get edit commit message from git subdirectory', async () => {
const expected = ['beta\n\n'];
const actual = await getMessages({edit: true});
expect(actual, 'to equal', expected);
});

test.serial('get history commit messages from shallow clone', async t => {
const [repo] = t.context.repos;

await writeFile('alpha.txt', 'alpha');
await execa('git', ['add', 'alpha.txt']);
await execa('git', ['commit', '-m', 'alpha']);

const clone = await cloneRepository(pkg.repository.url, repo, '--depth', '1');
t.context.repos = [...t.context.repos, clone];

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

async function initRepository() {
Expand All @@ -74,8 +95,21 @@ async function initRepository() {
return {directory, previous};
}

async function cloneRepository(source, context, ...args) {
const directory = join(tmpdir(), rand());
await execa('git', ['clone', ...args, source, directory]);
process.chdir(directory);

await execa('git', ['config', 'user.email', 'test@example.com']);
await execa('git', ['config', 'user.name', 'ava']);

return {directory, previous: context.previous};
}

async function cleanRepository(repo) {
process.chdir(repo.previous);
if (repo.previous && repo.previous !== process.cwd()) {
process.chdir(repo.previous);
}

if (await exists(repo.directory)) {
await rm(repo.directory);
Expand Down

0 comments on commit 8c354c5

Please sign in to comment.