Support hotfix version numbers (#28672)

diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart
index db71d60..e1b9923 100644
--- a/packages/flutter_tools/lib/src/version.dart
+++ b/packages/flutter_tools/lib/src/version.dart
@@ -539,11 +539,12 @@
 }
 
 class GitTagVersion {
-  const GitTagVersion(this.x, this.y, this.z, this.commits, this.hash);
+  const GitTagVersion(this.x, this.y, this.z, this.hotfix, this.commits, this.hash);
   const GitTagVersion.unknown()
     : x = null,
       y = null,
       z = null,
+      hotfix = null,
       commits = 0,
       hash = '';
 
@@ -556,6 +557,9 @@
   /// The Z in vX.Y.Z.
   final int z;
 
+  /// the F in vX.Y.Z-hotfix.F
+  final int hotfix;
+
   /// Number of commits since the vX.Y.Z tag.
   final int commits;
 
@@ -563,22 +567,30 @@
   final String hash;
 
   static GitTagVersion determine() {
-    final String version = _runGit('git describe --match v*.*.* --first-parent --long --tags');
-    final RegExp versionPattern = RegExp('^v([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)-g([a-f0-9]+)\$');
-    final List<String> parts = versionPattern.matchAsPrefix(version)?.groups(<int>[1, 2, 3, 4, 5]);
+    return parse(_runGit('git describe --match v*.*.* --first-parent --long --tags'));
+  }
+
+  static GitTagVersion parse(String version) {
+    final RegExp versionPattern = RegExp(r'^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-hotfix\.([0-9]+))?-([0-9]+)-g([a-f0-9]+)$');
+    final List<String> parts = versionPattern.matchAsPrefix(version)?.groups(<int>[1, 2, 3, 4, 5, 6]);
     if (parts == null) {
       printTrace('Could not interpret results of "git describe": $version');
       return const GitTagVersion.unknown();
     }
-    final List<int> parsedParts = parts.take(4).map<int>(int.tryParse).toList();
-    return GitTagVersion(parsedParts[0], parsedParts[1], parsedParts[2], parsedParts[3], parts[4]);
+    final List<int> parsedParts = parts.take(5).map<int>((String source) => source == null ? null : int.tryParse(source)).toList();
+    return GitTagVersion(parsedParts[0], parsedParts[1], parsedParts[2], parsedParts[3], parsedParts[4], parts[5]);
   }
 
   String frameworkVersionFor(String revision) {
     if (x == null || y == null || z == null || !revision.startsWith(hash))
       return '0.0.0-unknown';
-    if (commits == 0)
+    if (commits == 0) {
+      if (hotfix != null)
+        return '$x.$y.$z-hotfix.$hotfix';
       return '$x.$y.$z';
+    }
+    if (hotfix != null)
+      return '$x.$y.$z-hotfix.${hotfix + 1}-pre.$commits';
     return '$x.$y.${z + 1}-pre.$commits';
   }
 }
diff --git a/packages/flutter_tools/test/version_test.dart b/packages/flutter_tools/test/version_test.dart
index d25c57c..9a57f9c 100644
--- a/packages/flutter_tools/test/version_test.dart
+++ b/packages/flutter_tools/test/version_test.dart
@@ -388,6 +388,28 @@
       });
     });
   }
+
+  testUsingContext('GitTagVersion', () {
+    const String hash = 'abcdef';
+    expect(GitTagVersion.parse('v1.2.3-4-g$hash').frameworkVersionFor(hash), '1.2.4-pre.4');
+    expect(GitTagVersion.parse('v98.76.54-32-g$hash').frameworkVersionFor(hash), '98.76.55-pre.32');
+    expect(GitTagVersion.parse('v10.20.30-0-g$hash').frameworkVersionFor(hash), '10.20.30');
+    expect(GitTagVersion.parse('v1.2.3-hotfix.1-4-g$hash').frameworkVersionFor(hash), '1.2.3-hotfix.2-pre.4');
+    expect(GitTagVersion.parse('v7.2.4-hotfix.8-0-g$hash').frameworkVersionFor(hash), '7.2.4-hotfix.8');
+    expect(testLogger.traceText, '');
+    expect(GitTagVersion.parse('x1.2.3-4-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
+    expect(GitTagVersion.parse('v1.0.0-unknown-0-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
+    expect(GitTagVersion.parse('beta-1-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
+    expect(GitTagVersion.parse('v1.2.3-4-gx$hash').frameworkVersionFor(hash), '0.0.0-unknown');
+    expect(testLogger.statusText, '');
+    expect(testLogger.errorText, '');
+    expect(testLogger.traceText,
+      'Could not interpret results of "git describe": x1.2.3-4-gabcdef\n'
+      'Could not interpret results of "git describe": v1.0.0-unknown-0-gabcdef\n'
+      'Could not interpret results of "git describe": beta-1-gabcdef\n'
+      'Could not interpret results of "git describe": v1.2.3-4-gxabcdef\n'
+    );
+  });
 }
 
 void _expectVersionMessage(String message) {