diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart index a57787931d1..ae26f300a55 100644 --- a/packages/flutter_tools/lib/src/version.dart +++ b/packages/flutter_tools/lib/src/version.dart @@ -1049,23 +1049,47 @@ class GitTagVersion { } } - // If we're not currently on a tag, use git describe to find the most - // recent tag and number of commits past. - return parse( - git - .runSync([ - 'describe', - '--match', - '*.*.*', - '--long', - '--tags', - gitRef, - ], workingDirectory: workingDirectory) - .stdout - .trim(), + // If we don't exist in a tag, use git to find the latest tag. + return _useNewestTagAndCommitsPastFallback( + git: git, + workingDirectory: workingDirectory, + gitRef: gitRef, ); } + static GitTagVersion _useNewestTagAndCommitsPastFallback({ + required Git git, + required String? workingDirectory, + required String gitRef, + }) { + final String latestTag = git + .runSync([ + 'for-each-ref', + '--sort=-v:refname', + '--count=1', + '--format=%(refname:short)', + 'refs/tags/[0-9]*.*.*', + ], workingDirectory: workingDirectory) + .stdout + .trim(); + + final String ancestorRef = git + .runSync(['merge-base', gitRef, latestTag], workingDirectory: workingDirectory) + .stdout + .trim(); + + final String commitCount = git + .runSync([ + 'rev-list', + '--count', + '$ancestorRef..$gitRef', + ], workingDirectory: workingDirectory) + .stdout + .trim(); + + return parse('$latestTag-$commitCount'); + } + /// Parse a version string. /// /// The version string can either be an exact release tag (e.g. '1.2.3' for @@ -1109,6 +1133,7 @@ class GitTagVersion { ); } + @visibleForTesting static GitTagVersion parse(String version) { GitTagVersion gitTagVersion; diff --git a/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart b/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart index 05d07a83b39..f8c86e0d7c9 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart @@ -291,9 +291,8 @@ void main() { command: ['git', 'rev-parse', '--verify', '@{upstream}'], stdout: revision, ), - const FakeCommand(command: ['git', 'tag', '--points-at', revision]), const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', revision], + command: ['git', 'tag', '--points-at', revision], stdout: version, ), ]); diff --git a/packages/flutter_tools/test/general.shard/version_test.dart b/packages/flutter_tools/test/general.shard/version_test.dart index 2498032f8b1..3b08c903976 100644 --- a/packages/flutter_tools/test/general.shard/version_test.dart +++ b/packages/flutter_tools/test/general.shard/version_test.dart @@ -16,6 +16,7 @@ import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/git.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/version.dart'; +import 'package:meta/meta.dart'; import 'package:test/fake.dart'; import '../src/common.dart'; @@ -57,6 +58,34 @@ void main() { ); }); + /// Mocks the series of commands used to determine the Flutter version for `master`. + @useResult + List mockGitTagHistory({ + required String latestTag, + required String headRef, + required String ancestorRef, + required int commitsBetweenRefs, + }) { + return [ + FakeCommand( + command: const [ + 'git', + 'for-each-ref', + '--sort=-v:refname', + '--count=1', + '--format=%(refname:short)', + 'refs/tags/[0-9]*.*.*', + ], + stdout: latestTag, + ), + FakeCommand(command: ['git', 'merge-base', headRef, latestTag], stdout: ancestorRef), + FakeCommand( + command: ['git', 'rev-list', '--count', '$ancestorRef..$headRef'], + stdout: '$commitsBetweenRefs', + ), + ]; + } + for (final String channel in kOfficialChannels) { DateTime getChannelUpToDateVersion() { return _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate(channel) ~/ 2); @@ -100,17 +129,11 @@ void main() { stdout: '1234abcd', ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: [ - 'git', - 'describe', - '--match', - '*.*.*', - '--long', - '--tags', - '1234abcd', - ], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '', + headRef: '1234abcd', + ancestorRef: '', + commitsBetweenRefs: 0, ), FakeCommand( command: const ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -278,17 +301,11 @@ void main() { ], ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: [ - 'git', - 'describe', - '--match', - '*.*.*', - '--long', - '--tags', - '1234abcd', - ], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '0.1.2-3', + headRef: '1234abcd', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 170, ), FakeCommand( command: const ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -467,17 +484,11 @@ void main() { stdout: '1234abcd', ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: [ - 'git', - 'describe', - '--match', - '*.*.*', - '--long', - '--tags', - '1234abcd', - ], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '0.1.2-3', + headRef: '1234abcd', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 170, ), FakeCommand( command: const ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -859,9 +870,11 @@ void main() { stdout: '1234abcd', ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '0.1.2-3', + headRef: '1234abcd', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 170, ), const FakeCommand( command: ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -907,9 +920,11 @@ void main() { stdout: '1234abcd', ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '0.1.2-3', + headRef: '1234abcd', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 170, ), const FakeCommand( command: ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -1148,9 +1163,11 @@ void main() { stdout: '1234abcd', ), const FakeCommand(command: ['git', 'tag', '--points-at', '1234abcd']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'], - stdout: '0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: '0.1.2-3', + headRef: '1234abcd', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 170, ), const FakeCommand( command: ['git', 'symbolic-ref', '--short', 'HEAD'], @@ -1400,15 +1417,16 @@ void main() { testUsingContext('determine reports correct git describe version if HEAD is not at a tag', () { const devTag = '1.2.0-2.0.pre'; const headRevision = 'abcd1234'; - const commitsAhead = '12'; processManager.addCommands([ const FakeCommand( command: ['git', 'tag', '--points-at', 'HEAD'], // no output, since there's no tag ), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'], - stdout: '$devTag-$commitsAhead-g$headRevision', + ...mockGitTagHistory( + latestTag: devTag, + headRef: 'HEAD', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 12, ), ]); final platform = FakePlatform(); @@ -1425,9 +1443,11 @@ void main() { testUsingContext('determine does not call fetch --tags', () { processManager.addCommands([ const FakeCommand(command: ['git', 'tag', '--points-at', 'HEAD']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'], - stdout: 'v0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: 'v0.1.2-3', + headRef: 'HEAD', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 12, ), ]); final platform = FakePlatform(); @@ -1443,9 +1463,11 @@ void main() { stdout: 'beta', ), const FakeCommand(command: ['git', 'tag', '--points-at', 'HEAD']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'], - stdout: 'v0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: 'v0.1.2-3', + headRef: 'HEAD', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 12, ), ]); final platform = FakePlatform(); @@ -1464,9 +1486,11 @@ void main() { command: ['git', 'fetch', 'https://github.com/flutter/flutter.git', '--tags', '-f'], ), const FakeCommand(command: ['git', 'tag', '--points-at', 'HEAD']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'], - stdout: 'v0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: 'v0.1.2-3', + headRef: 'HEAD', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 12, ), ]); final platform = FakePlatform(); @@ -1485,9 +1509,11 @@ void main() { command: ['git', 'fetch', 'https://githubmirror.com/flutter.git', '--tags', '-f'], ), const FakeCommand(command: ['git', 'tag', '--points-at', 'HEAD']), - const FakeCommand( - command: ['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'], - stdout: 'v0.1.2-3-1234abcd', + ...mockGitTagHistory( + latestTag: 'v0.1.2-3', + headRef: 'HEAD', + ancestorRef: 'abcd1234', + commitsBetweenRefs: 12, ), ]); final platform = FakePlatform(