Skip to content

Commit 1a3f294

Browse files
committed
git: resolve detached tag refs exactly
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent 91d6d42 commit 1a3f294

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

__tests__/git.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ describe('ref', () => {
122122
case 'git show -s --pretty=%D':
123123
result = 'HEAD, tag: 8.0.0';
124124
break;
125+
case 'git for-each-ref --format=%(refname) --points-at HEAD refs/tags/':
126+
result = 'refs/tags/8.0.0';
127+
break;
125128
}
126129
return Promise.resolve({
127130
stdout: result,
@@ -144,6 +147,9 @@ describe('ref', () => {
144147
case 'git show -s --pretty=%D':
145148
result = 'grafted, HEAD, tag: 8.0.0';
146149
break;
150+
case 'git for-each-ref --format=%(refname) --points-at HEAD refs/tags/':
151+
result = 'refs/tags/8.0.0';
152+
break;
147153
}
148154
return Promise.resolve({
149155
stdout: result,
@@ -420,6 +426,9 @@ describe('ref', () => {
420426
case 'git show -s --pretty=%D':
421427
result = 'HEAD, tag: v8.0.0, origin/release-branch';
422428
break;
429+
case 'git for-each-ref --format=%(refname) --points-at HEAD refs/tags/':
430+
result = 'refs/tags/v8.0.0';
431+
break;
423432
}
424433
return Promise.resolve({
425434
stdout: result,
@@ -442,6 +451,9 @@ describe('ref', () => {
442451
case 'git show -s --pretty=%D':
443452
result = 'grafted, HEAD, tag: v8.0.0, origin/release-branch';
444453
break;
454+
case 'git for-each-ref --format=%(refname) --points-at HEAD refs/tags/':
455+
result = 'refs/tags/v8.0.0';
456+
break;
445457
}
446458
return Promise.resolve({
447459
stdout: result,
@@ -452,6 +464,31 @@ describe('ref', () => {
452464
const ref = await Git.ref();
453465
expect(ref).toEqual('refs/tags/v8.0.0');
454466
});
467+
468+
it('returns mocked detached tag ref when tag name contains a comma', async () => {
469+
vi.spyOn(Exec, 'getExecOutput').mockImplementation((cmd, args): Promise<ExecOutput> => {
470+
const fullCmd = `${cmd} ${args?.join(' ')}`;
471+
let result = '';
472+
switch (fullCmd) {
473+
case 'git branch --show-current':
474+
result = '';
475+
break;
476+
case 'git show -s --pretty=%D':
477+
result = 'HEAD, tag: release,with-comma, origin/release-branch';
478+
break;
479+
case 'git for-each-ref --format=%(refname) --points-at HEAD refs/tags/':
480+
result = 'refs/tags/release,with-comma';
481+
break;
482+
}
483+
return Promise.resolve({
484+
stdout: result,
485+
stderr: '',
486+
exitCode: 0
487+
});
488+
});
489+
const ref = await Git.ref();
490+
expect(ref).toEqual('refs/tags/release,with-comma');
491+
});
455492
});
456493

457494
describe('fullCommit', () => {

src/git.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class Git {
138138

139139
// Tag refs are formatted as "tag: <tagname>"
140140
if (ref.startsWith('tag: ')) {
141-
return `refs/tags/${ref.slice('tag: '.length).split(',')[0].trim()}`;
141+
return await Git.findDetachedTagRef(ref, res);
142142
}
143143

144144
// Pull request merge refs are formatted as "pull/<number>/<state>"
@@ -201,6 +201,28 @@ export class Git {
201201
throw new Error(`Cannot infer ref from detached HEAD`);
202202
}
203203

204+
private static async findDetachedTagRef(tagDecoration: string, originalRef: string): Promise<string> {
205+
const tagRefs = await Git.exec(['for-each-ref', '--format=%(refname)', '--points-at', 'HEAD', 'refs/tags/']);
206+
const refs = tagRefs
207+
.split('\n')
208+
.map(tagRef => tagRef.trim())
209+
.filter(tagRef => tagRef.length > 0)
210+
.sort((a, b) => b.length - a.length);
211+
212+
for (const tagRef of refs) {
213+
const decoration = `tag: ${tagRef.slice('refs/tags/'.length)}`;
214+
if (tagDecoration === decoration || tagDecoration.startsWith(`${decoration}, `)) {
215+
return tagRef;
216+
}
217+
}
218+
219+
if (refs.length === 1) {
220+
return refs[0];
221+
}
222+
223+
throw new Error(`Cannot find detached tag ref in "${originalRef}"`);
224+
}
225+
204226
private static async findContainingRef(scope: string): Promise<string | undefined> {
205227
const refs = await Git.exec(['for-each-ref', '--format=%(refname)', '--contains', 'HEAD', '--sort=-committerdate', scope]);
206228

0 commit comments

Comments
 (0)