Skip to content

Commit 716b7e6

Browse files
committed
fix: retrieve branch from repo with a detached head
1 parent b0a335c commit 716b7e6

File tree

5 files changed

+88
-17
lines changed

5 files changed

+88
-17
lines changed

lib/git.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ function head() {
1717

1818
function branch() {
1919
try {
20-
return execa.sync('git', ['rev-parse', '--abbrev-ref', 'HEAD']).stdout;
20+
const branch = execa
21+
.sync('git', ['show', '-s', '--pretty=%d', 'HEAD'])
22+
.stdout.match(/\(?(.*)\)?/)[1]
23+
.split(', ')
24+
.find(branch => branch.startsWith('origin/'));
25+
26+
return branch ? branch.match(/^origin\/(.+)/)[1] : execa.sync('git', ['rev-parse', '--abbrev-ref', 'HEAD']).stdout;
2127
} catch (err) {
2228
return undefined;
2329
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"devDependencies": {
1414
"ava": "^0.25.0",
1515
"codecov": "^3.0.0",
16+
"file-url": "^2.0.2",
1617
"nyc": "^11.1.0",
1718
"semantic-release": "^15.0.0",
1819
"tempy": "^0.2.1",

test/codebuild.test.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import test from 'ava';
22
import codebuild from '../lib/codebuild';
3-
import {gitRepo, gitCommit} from './helpers/git-utils';
3+
import {gitRepo, gitHead} from './helpers/git-utils';
44

55
// Save the current working diretory
66
const cwd = process.cwd();
77

88
test.beforeEach(async () => {
9-
await gitRepo();
9+
await gitRepo(true);
1010
});
1111

1212
test.afterEach.always(() => {
@@ -15,15 +15,14 @@ test.afterEach.always(() => {
1515
});
1616

1717
test('Push', async t => {
18-
const commit = await gitCommit();
1918
process.env.CODEBUILD_BUILD_ID = 'env-ci:40cc72d2-acd5-46f4-a86b-6a3dcd2a39a0';
2019
process.env.AWS_REGION = 'us-east-1';
2120
process.env.PWD = '/codebuild/output/src807365521/src/github.com/owner/repo';
2221

2322
t.deepEqual(codebuild.configuration(), {
2423
name: 'AWS CodeBuild',
2524
service: 'codebuild',
26-
commit,
25+
commit: await gitHead(),
2726
build: 'env-ci:40cc72d2-acd5-46f4-a86b-6a3dcd2a39a0',
2827
branch: 'master',
2928
buildUrl:

test/git.test.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import test from 'ava';
22
import git from '../lib/git';
3-
import {gitRepo, gitCommit} from './helpers/git-utils';
3+
import {gitRepo, gitCommit, gitHead} from './helpers/git-utils';
44

55
// Save the current working diretory
66
const cwd = process.cwd();
77

8-
test.beforeEach(async () => {
9-
await gitRepo('master');
10-
});
11-
128
test.afterEach.always(() => {
139
// Restore the current working directory
1410
process.chdir(cwd);
1511
});
1612

1713
test.serial('Git repository', async t => {
14+
await gitRepo();
1815
const commit = await gitCommit();
1916

2017
t.deepEqual(git.configuration(), {commit, branch: 'master'});
2118
});
19+
20+
test.serial('Git repository with detached head', async t => {
21+
await gitRepo(true);
22+
23+
t.deepEqual(git.configuration(), {commit: await gitHead(), branch: 'master'});
24+
});

test/helpers/git-utils.js

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,77 @@
11
import tempy from 'tempy';
22
import execa from 'execa';
3+
import fileUrl from 'file-url';
34

45
/**
5-
* Create a git repository and change the current working directory to the repository root.
6+
* Create a temporary git repository.
7+
* If `withRemote` is `true`, creates a bare repository, initialize it and create a shallow clone. Change the current working directory to the clone root.
8+
* If `withRemote` is `false`, creates a regular repository and initialize it. Change the current working directory to the repository root.
69
*
7-
* @method gitRepo
8-
* @param {String} [branch='master'] The branch to initialize.
9-
* @return {String} The path of the repository otherwise.
10+
* @param {Boolean} withRemote `true` to create a shallow clone of a bare repository.
11+
* @param {String} [branc='master'] The branch to initialize.
12+
* @return {String} The path of the clone if `withRemote` is `true`, the path of the repository otherwise.
1013
*/
11-
export async function gitRepo(branch = 'master') {
14+
export async function gitRepo(withRemote, branch = 'master') {
1215
const dir = tempy.directory();
1316

1417
process.chdir(dir);
15-
await execa('git', ['init']);
16-
await execa('git', ['checkout', '-b', branch]);
18+
await execa('git', ['init'].concat(withRemote ? ['--bare'] : []));
19+
20+
if (withRemote) {
21+
await initBareRepo(fileUrl(dir), branch);
22+
await gitShallowClone(fileUrl(dir));
23+
} else {
24+
await gitCheckout(branch);
25+
}
26+
return fileUrl(dir);
27+
}
28+
29+
/**
30+
* Initialize an existing bare repository:
31+
* - Clone the repository
32+
* - Change the current working directory to the clone root
33+
* - Create a default branch
34+
* - Create an initial commits
35+
* - Push to origin
36+
*
37+
* @param {String} origin The URL of the bare repository.
38+
* @param {String} [branch='master'] the branch to initialize.
39+
*/
40+
export async function initBareRepo(origin, branch = 'master') {
41+
const clone = tempy.directory();
42+
await execa('git', ['clone', '--no-hardlinks', origin, clone]);
43+
process.chdir(clone);
44+
await gitCheckout(branch);
45+
await gitCommit('Initial commit');
46+
await execa('git', ['push', origin, branch]);
47+
}
48+
49+
/**
50+
* Create a shallow clone of a git repository and change the current working directory to the cloned repository root.
51+
* The shallow will contain a limited number of commit and no tags.
52+
*
53+
* @param {String} origin The path of the repository to clone.
54+
* @param {Number} [depth=1] The number of commit to clone.
55+
* @return {String} The path of the cloned repository.
56+
*/
57+
export async function gitShallowClone(origin, branch = 'master', depth = 1) {
58+
const dir = tempy.directory();
59+
60+
process.chdir(dir);
61+
await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, origin, dir]);
1762
return dir;
1863
}
1964

65+
/**
66+
* Checkout a branch on the current git repository.
67+
*
68+
* @param {String} branch Branch name.
69+
* @param {boolean} create `true` to create the branche ans switch, `false` to only switch.
70+
*/
71+
export async function gitCheckout(branch, create = true) {
72+
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch]);
73+
}
74+
2075
/**
2176
* Create commit on the current git repository.
2277
*
@@ -28,3 +83,10 @@ export async function gitCommit(message = 'Test commit message') {
2883
await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign']);
2984
return execa.stdout('git', ['rev-parse', 'HEAD']);
3085
}
86+
87+
/**
88+
* @return {String} The sha of the head commit in the current git repository.
89+
*/
90+
export async function gitHead() {
91+
return execa.stdout('git', ['rev-parse', 'HEAD']);
92+
}

0 commit comments

Comments
 (0)