Skip to content

Commit 74233b1

Browse files
jasonlearstviceice
andauthored
feat(git-refs): use dereferenced commit hash for annotated tags (renovatebot#41560)
* feat(git-refs): use dereferenced commit hash for annotated tags When git ls-remote lists annotated tags, it returns two entries: 1. The tag object hash: refs/tags/v1.0.0 -> abc123 2. The dereferenced commit hash: refs/tags/v1.0.0^{} -> def456 Previously, Renovate used the tag object hash (abc123). However, `git submodule status` returns the actual commit hash (def456). This mismatch caused false-positive dependency updates that resulted in empty commits, since the tag object and commit hash differ even when pointing to the same version. This feature: - Parses both entries from git ls-remote into allRefs - Builds a lookup map of tag names to their dereferenced commit hashes - For annotated tags, replaces the tag object hash with the commit hash - Lightweight tags are unaffected (they already return the commit hash) This ensures Renovate's detected digest matches what git submodule operations actually use, preventing unnecessary update PRs. * Apply suggestions from code review Co-authored-by: Michael Kriese <michael.kriese@gmx.de> * fix typo in dereferencedTags variable name --------- Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
1 parent 2f71422 commit 74233b1

File tree

5 files changed

+43
-16
lines changed

5 files changed

+43
-16
lines changed

lib/modules/datasource/git-refs/__snapshots__/index.spec.ts.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,32 @@ exports[`modules/datasource/git-refs/index > getReleases > returns versions filt
55
"releases": [
66
{
77
"gitRef": "v1.0.0",
8-
"newDigest": "a9920c014aebc28dc1b23e7efcc006d045512345",
8+
"newDigest": "7b756026fb2de270240a889a413e7e3a9d4d4d85",
99
"version": "v1.0.0",
1010
},
1111
{
1212
"gitRef": "v1.0.1",
13-
"newDigest": "281fbfb58990ec98b237a923d67904c102bec34c",
13+
"newDigest": "e173183f932ba8a31d0e4f23cc1070e8ebfa59d6",
1414
"version": "v1.0.1",
1515
},
1616
{
1717
"gitRef": "v1.0.2",
18-
"newDigest": "9cb93e0b236385a4e2efd089d7c6a458f5ff321f",
18+
"newDigest": "3936a6bced3587dc9fd464b0a910e0dfd4cfe10d",
1919
"version": "v1.0.2",
2020
},
2121
{
2222
"gitRef": "v1.0.3",
23-
"newDigest": "8b0d0e0aec21ea059448ef0255387dbb82c61973",
23+
"newDigest": "125ca9f3df4151e50046e5327ecb29ec4c13efab",
2424
"version": "v1.0.3",
2525
},
2626
{
2727
"gitRef": "v1.0.4",
28-
"newDigest": "2b52829c7c1bd65b3501c450849c53b90b11fa0e",
28+
"newDigest": "3ed9e7d7094fd4ee7751c24a3e6b706060f461ff",
2929
"version": "v1.0.4",
3030
},
3131
{
3232
"gitRef": "v1.0.5",
33-
"newDigest": "2d138c34e4c6939d0a8686943e851c6528aa04db",
33+
"newDigest": "6d7a933c2e6b7b39e992b1f93b6b42de083b28f0",
3434
"version": "v1.0.5",
3535
},
3636
],

lib/modules/datasource/git-refs/base.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export abstract class GitDatasource extends Datasource {
4141
return null;
4242
}
4343

44-
const refs = lsRemote
44+
const allRefs = lsRemote
4545
.trim()
4646
.split(newlineRegex)
4747
.map((line) => line.trim())
@@ -66,7 +66,32 @@ export abstract class GitDatasource extends Datasource {
6666
return null;
6767
})
6868
.filter(isTruthy)
69-
.filter((ref) => ref.type !== 'pull' && !ref.value.endsWith('^{}'));
69+
.filter((ref) => ref.type !== 'pull');
70+
71+
// For annotated tags, git ls-remote returns two entries:
72+
// 1. The tag object hash: refs/tags/v1.0.0
73+
// 2. The dereferenced commit hash: refs/tags/v1.0.0^{}
74+
// We need to use the dereferenced commit hash (^{}) for annotated tags
75+
// to match what `git submodule status` returns (the actual commit hash).
76+
// This prevents false-positive updates that result in empty commits.
77+
const dereferencedTags: Record<string, string> = {};
78+
for (const ref of allRefs) {
79+
if (ref.value.endsWith('^{}')) {
80+
// Store the commit hash for the base tag name (without ^{})
81+
dereferencedTags[ref.value.slice(0, -3)] = ref.hash;
82+
}
83+
}
84+
85+
const refs = allRefs
86+
.filter((ref) => !ref.value.endsWith('^{}'))
87+
.map((ref) => {
88+
// For annotated tags, use the dereferenced commit hash
89+
const dereferencedHash = dereferencedTags[ref.value];
90+
if (dereferencedHash) {
91+
return { ...ref, hash: dereferencedHash };
92+
}
93+
return ref;
94+
});
7095

7196
return refs;
7297
}

lib/modules/datasource/git-refs/index.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ describe('modules/datasource/git-refs/index', () => {
9898
{ packageName: 'a tag to look up' },
9999
'v1.0.4',
100100
);
101-
expect(digest).toBe('2b52829c7c1bd65b3501c450849c53b90b11fa0e');
101+
// For annotated tags, we return the dereferenced commit hash (^{})
102+
// to match what `git submodule status` returns
103+
expect(digest).toBe('3ed9e7d7094fd4ee7751c24a3e6b706060f461ff');
102104
});
103105

104106
it('ignores refs/for/', async () => {

lib/modules/datasource/git-tags/__snapshots__/index.spec.ts.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,32 @@ exports[`modules/datasource/git-tags/index > getReleases > returns versions filt
55
"releases": [
66
{
77
"gitRef": "v1.0.0",
8-
"newDigest": "0be9ffb1ae0c8d0846cd88b58dfef42d74674673",
8+
"newDigest": "7b756026fb2de270240a889a413e7e3a9d4d4d85",
99
"version": "v1.0.0",
1010
},
1111
{
1212
"gitRef": "v1.0.1",
13-
"newDigest": "281fbfb58990ec98b237a923d67904c102bec34c",
13+
"newDigest": "e173183f932ba8a31d0e4f23cc1070e8ebfa59d6",
1414
"version": "v1.0.1",
1515
},
1616
{
1717
"gitRef": "v1.0.2",
18-
"newDigest": "9cb93e0b236385a4e2efd089d7c6a458f5ff321f",
18+
"newDigest": "3936a6bced3587dc9fd464b0a910e0dfd4cfe10d",
1919
"version": "v1.0.2",
2020
},
2121
{
2222
"gitRef": "v1.0.3",
23-
"newDigest": "8b0d0e0aec21ea059448ef0255387dbb82c61973",
23+
"newDigest": "125ca9f3df4151e50046e5327ecb29ec4c13efab",
2424
"version": "v1.0.3",
2525
},
2626
{
2727
"gitRef": "v1.0.4",
28-
"newDigest": "2b52829c7c1bd65b3501c450849c53b90b11fa0e",
28+
"newDigest": "3ed9e7d7094fd4ee7751c24a3e6b706060f461ff",
2929
"version": "v1.0.4",
3030
},
3131
{
3232
"gitRef": "v1.0.5",
33-
"newDigest": "2d138c34e4c6939d0a8686943e851c6528aa04db",
33+
"newDigest": "6d7a933c2e6b7b39e992b1f93b6b42de083b28f0",
3434
"version": "v1.0.5",
3535
},
3636
],

lib/modules/datasource/git-tags/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ describe('modules/datasource/git-tags/index', () => {
8181
{ packageName: 'a tag to look up' },
8282
'v1.0.2',
8383
);
84-
expect(digest).toBe('9cb93e0b236385a4e2efd089d7c6a458f5ff321f');
84+
expect(digest).toBe('3936a6bced3587dc9fd464b0a910e0dfd4cfe10d');
8585
});
8686

8787
it('returns digest for HEAD', async () => {

0 commit comments

Comments
 (0)