Skip to content

Commit e45fd36

Browse files
authored
Use an alternative to git describe for master version resolution (#174088)
Closes #173904. It's not clear to me how `git describe --tags HEAD` ever ... worked to determine a fallback. From what I can tell, the _intent_ was to use the latest (newest? closest?) tag as the base version, and then append `-{commitCount}-{shortHash}` as the fallback version number when on `master` (or any non-published branch). So, I rewrote the implementation, unfortunately with 4 separate calls out to `git ...` instead of a single one. There are 20+ tests that fail as a result of this change, mostly because they make specific expectations around `git describe` being invoked, and of course that is no longer the case - putting those aside, I'd like to double check that: 1. I understand what the original command was _intending_ to do 2. We like the _output_ of the updated command 3. ... we either are okay with the implementation, or have an alternative in mind (I'm no `git` master) Wdyt? At this commit: ```sh $ flutter-dev --version Flutter 3.36.0-1.0.pre-170 • channel [user-branch] • https://github.com/matanlurey/flutter Framework • revision 250381a (5 minutes ago) • 2025-08-19 18:57:36 -0700 Engine • hash f278b0aa3b8c6732ab636563eb8e896c35fc9c79 (revision 9ac4fac) (2 hours ago) • 2025-08-19 23:42:28.000Z Tools • Dart 3.10.0 (build 3.10.0-115.0.dev) • DevTools 2.49.0 ``` /cc @zanderso @jmagman for historics.
1 parent 4d4a69f commit e45fd36

File tree

3 files changed

+124
-74
lines changed

3 files changed

+124
-74
lines changed

packages/flutter_tools/lib/src/version.dart

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,23 +1049,47 @@ class GitTagVersion {
10491049
}
10501050
}
10511051

1052-
// If we're not currently on a tag, use git describe to find the most
1053-
// recent tag and number of commits past.
1054-
return parse(
1055-
git
1056-
.runSync([
1057-
'describe',
1058-
'--match',
1059-
'*.*.*',
1060-
'--long',
1061-
'--tags',
1062-
gitRef,
1063-
], workingDirectory: workingDirectory)
1064-
.stdout
1065-
.trim(),
1052+
// If we don't exist in a tag, use git to find the latest tag.
1053+
return _useNewestTagAndCommitsPastFallback(
1054+
git: git,
1055+
workingDirectory: workingDirectory,
1056+
gitRef: gitRef,
10661057
);
10671058
}
10681059

1060+
static GitTagVersion _useNewestTagAndCommitsPastFallback({
1061+
required Git git,
1062+
required String? workingDirectory,
1063+
required String gitRef,
1064+
}) {
1065+
final String latestTag = git
1066+
.runSync([
1067+
'for-each-ref',
1068+
'--sort=-v:refname',
1069+
'--count=1',
1070+
'--format=%(refname:short)',
1071+
'refs/tags/[0-9]*.*.*',
1072+
], workingDirectory: workingDirectory)
1073+
.stdout
1074+
.trim();
1075+
1076+
final String ancestorRef = git
1077+
.runSync(['merge-base', gitRef, latestTag], workingDirectory: workingDirectory)
1078+
.stdout
1079+
.trim();
1080+
1081+
final String commitCount = git
1082+
.runSync([
1083+
'rev-list',
1084+
'--count',
1085+
'$ancestorRef..$gitRef',
1086+
], workingDirectory: workingDirectory)
1087+
.stdout
1088+
.trim();
1089+
1090+
return parse('$latestTag-$commitCount');
1091+
}
1092+
10691093
/// Parse a version string.
10701094
///
10711095
/// The version string can either be an exact release tag (e.g. '1.2.3' for
@@ -1109,6 +1133,7 @@ class GitTagVersion {
11091133
);
11101134
}
11111135

1136+
@visibleForTesting
11121137
static GitTagVersion parse(String version) {
11131138
GitTagVersion gitTagVersion;
11141139

packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,8 @@ void main() {
291291
command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
292292
stdout: revision,
293293
),
294-
const FakeCommand(command: <String>['git', 'tag', '--points-at', revision]),
295294
const FakeCommand(
296-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', revision],
295+
command: <String>['git', 'tag', '--points-at', revision],
297296
stdout: version,
298297
),
299298
]);

packages/flutter_tools/test/general.shard/version_test.dart

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/features.dart';
1616
import 'package:flutter_tools/src/git.dart';
1717
import 'package:flutter_tools/src/globals.dart' as globals;
1818
import 'package:flutter_tools/src/version.dart';
19+
import 'package:meta/meta.dart';
1920
import 'package:test/fake.dart';
2021

2122
import '../src/common.dart';
@@ -57,6 +58,34 @@ void main() {
5758
);
5859
});
5960

61+
/// Mocks the series of commands used to determine the Flutter version for `master`.
62+
@useResult
63+
List<FakeCommand> mockGitTagHistory({
64+
required String latestTag,
65+
required String headRef,
66+
required String ancestorRef,
67+
required int commitsBetweenRefs,
68+
}) {
69+
return [
70+
FakeCommand(
71+
command: const [
72+
'git',
73+
'for-each-ref',
74+
'--sort=-v:refname',
75+
'--count=1',
76+
'--format=%(refname:short)',
77+
'refs/tags/[0-9]*.*.*',
78+
],
79+
stdout: latestTag,
80+
),
81+
FakeCommand(command: ['git', 'merge-base', headRef, latestTag], stdout: ancestorRef),
82+
FakeCommand(
83+
command: ['git', 'rev-list', '--count', '$ancestorRef..$headRef'],
84+
stdout: '$commitsBetweenRefs',
85+
),
86+
];
87+
}
88+
6089
for (final String channel in kOfficialChannels) {
6190
DateTime getChannelUpToDateVersion() {
6291
return _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate(channel) ~/ 2);
@@ -100,17 +129,11 @@ void main() {
100129
stdout: '1234abcd',
101130
),
102131
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
103-
const FakeCommand(
104-
command: <String>[
105-
'git',
106-
'describe',
107-
'--match',
108-
'*.*.*',
109-
'--long',
110-
'--tags',
111-
'1234abcd',
112-
],
113-
stdout: '0.1.2-3-1234abcd',
132+
...mockGitTagHistory(
133+
latestTag: '',
134+
headRef: '1234abcd',
135+
ancestorRef: '',
136+
commitsBetweenRefs: 0,
114137
),
115138
FakeCommand(
116139
command: const <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -278,17 +301,11 @@ void main() {
278301
],
279302
),
280303
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
281-
const FakeCommand(
282-
command: <String>[
283-
'git',
284-
'describe',
285-
'--match',
286-
'*.*.*',
287-
'--long',
288-
'--tags',
289-
'1234abcd',
290-
],
291-
stdout: '0.1.2-3-1234abcd',
304+
...mockGitTagHistory(
305+
latestTag: '0.1.2-3',
306+
headRef: '1234abcd',
307+
ancestorRef: 'abcd1234',
308+
commitsBetweenRefs: 170,
292309
),
293310
FakeCommand(
294311
command: const <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -467,17 +484,11 @@ void main() {
467484
stdout: '1234abcd',
468485
),
469486
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
470-
const FakeCommand(
471-
command: <String>[
472-
'git',
473-
'describe',
474-
'--match',
475-
'*.*.*',
476-
'--long',
477-
'--tags',
478-
'1234abcd',
479-
],
480-
stdout: '0.1.2-3-1234abcd',
487+
...mockGitTagHistory(
488+
latestTag: '0.1.2-3',
489+
headRef: '1234abcd',
490+
ancestorRef: 'abcd1234',
491+
commitsBetweenRefs: 170,
481492
),
482493
FakeCommand(
483494
command: const <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -859,9 +870,11 @@ void main() {
859870
stdout: '1234abcd',
860871
),
861872
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
862-
const FakeCommand(
863-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'],
864-
stdout: '0.1.2-3-1234abcd',
873+
...mockGitTagHistory(
874+
latestTag: '0.1.2-3',
875+
headRef: '1234abcd',
876+
ancestorRef: 'abcd1234',
877+
commitsBetweenRefs: 170,
865878
),
866879
const FakeCommand(
867880
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -907,9 +920,11 @@ void main() {
907920
stdout: '1234abcd',
908921
),
909922
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
910-
const FakeCommand(
911-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'],
912-
stdout: '0.1.2-3-1234abcd',
923+
...mockGitTagHistory(
924+
latestTag: '0.1.2-3',
925+
headRef: '1234abcd',
926+
ancestorRef: 'abcd1234',
927+
commitsBetweenRefs: 170,
913928
),
914929
const FakeCommand(
915930
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -1148,9 +1163,11 @@ void main() {
11481163
stdout: '1234abcd',
11491164
),
11501165
const FakeCommand(command: <String>['git', 'tag', '--points-at', '1234abcd']),
1151-
const FakeCommand(
1152-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'],
1153-
stdout: '0.1.2-3-1234abcd',
1166+
...mockGitTagHistory(
1167+
latestTag: '0.1.2-3',
1168+
headRef: '1234abcd',
1169+
ancestorRef: 'abcd1234',
1170+
commitsBetweenRefs: 170,
11541171
),
11551172
const FakeCommand(
11561173
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
@@ -1400,15 +1417,16 @@ void main() {
14001417
testUsingContext('determine reports correct git describe version if HEAD is not at a tag', () {
14011418
const devTag = '1.2.0-2.0.pre';
14021419
const headRevision = 'abcd1234';
1403-
const commitsAhead = '12';
14041420
processManager.addCommands(<FakeCommand>[
14051421
const FakeCommand(
14061422
command: <String>['git', 'tag', '--points-at', 'HEAD'],
14071423
// no output, since there's no tag
14081424
),
1409-
const FakeCommand(
1410-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
1411-
stdout: '$devTag-$commitsAhead-g$headRevision',
1425+
...mockGitTagHistory(
1426+
latestTag: devTag,
1427+
headRef: 'HEAD',
1428+
ancestorRef: 'abcd1234',
1429+
commitsBetweenRefs: 12,
14121430
),
14131431
]);
14141432
final platform = FakePlatform();
@@ -1425,9 +1443,11 @@ void main() {
14251443
testUsingContext('determine does not call fetch --tags', () {
14261444
processManager.addCommands(<FakeCommand>[
14271445
const FakeCommand(command: <String>['git', 'tag', '--points-at', 'HEAD']),
1428-
const FakeCommand(
1429-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
1430-
stdout: 'v0.1.2-3-1234abcd',
1446+
...mockGitTagHistory(
1447+
latestTag: 'v0.1.2-3',
1448+
headRef: 'HEAD',
1449+
ancestorRef: 'abcd1234',
1450+
commitsBetweenRefs: 12,
14311451
),
14321452
]);
14331453
final platform = FakePlatform();
@@ -1443,9 +1463,11 @@ void main() {
14431463
stdout: 'beta',
14441464
),
14451465
const FakeCommand(command: <String>['git', 'tag', '--points-at', 'HEAD']),
1446-
const FakeCommand(
1447-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
1448-
stdout: 'v0.1.2-3-1234abcd',
1466+
...mockGitTagHistory(
1467+
latestTag: 'v0.1.2-3',
1468+
headRef: 'HEAD',
1469+
ancestorRef: 'abcd1234',
1470+
commitsBetweenRefs: 12,
14491471
),
14501472
]);
14511473
final platform = FakePlatform();
@@ -1464,9 +1486,11 @@ void main() {
14641486
command: <String>['git', 'fetch', 'https://github.com/flutter/flutter.git', '--tags', '-f'],
14651487
),
14661488
const FakeCommand(command: <String>['git', 'tag', '--points-at', 'HEAD']),
1467-
const FakeCommand(
1468-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
1469-
stdout: 'v0.1.2-3-1234abcd',
1489+
...mockGitTagHistory(
1490+
latestTag: 'v0.1.2-3',
1491+
headRef: 'HEAD',
1492+
ancestorRef: 'abcd1234',
1493+
commitsBetweenRefs: 12,
14701494
),
14711495
]);
14721496
final platform = FakePlatform();
@@ -1485,9 +1509,11 @@ void main() {
14851509
command: <String>['git', 'fetch', 'https://githubmirror.com/flutter.git', '--tags', '-f'],
14861510
),
14871511
const FakeCommand(command: <String>['git', 'tag', '--points-at', 'HEAD']),
1488-
const FakeCommand(
1489-
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
1490-
stdout: 'v0.1.2-3-1234abcd',
1512+
...mockGitTagHistory(
1513+
latestTag: 'v0.1.2-3',
1514+
headRef: 'HEAD',
1515+
ancestorRef: 'abcd1234',
1516+
commitsBetweenRefs: 12,
14911517
),
14921518
]);
14931519
final platform = FakePlatform(

0 commit comments

Comments
 (0)