Skip to content

Commit 4552af1

Browse files
[flutter_tools] enable flutter upgrade to support force pushed branches (flutter#55594)
1 parent f37a91a commit 4552af1

File tree

3 files changed

+62
-119
lines changed

3 files changed

+62
-119
lines changed

packages/flutter_tools/lib/src/commands/upgrade.dart

+31-47
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ class UpgradeCommandRunner {
100100
@required FlutterVersion flutterVersion,
101101
@required bool testFlow,
102102
}) async {
103-
await verifyUpstreamConfigured();
103+
final String upstreamRevision = await fetchRemoteRevision();
104+
if (flutterVersion.frameworkRevision == upstreamRevision) {
105+
globals.printStatus('Flutter is already up to date on channel ${flutterVersion.channel}');
106+
globals.printStatus('$flutterVersion');
107+
return;
108+
}
104109
if (!force && gitTagVersion == const GitTagVersion.unknown()) {
105110
// If the commit is a recognized branch and not master,
106111
// explain that we are avoiding potential damage.
@@ -132,12 +137,8 @@ class UpgradeCommandRunner {
132137
}
133138
recordState(flutterVersion);
134139
await upgradeChannel(flutterVersion);
135-
final bool alreadyUpToDate = await attemptFastForward(flutterVersion);
136-
if (alreadyUpToDate) {
137-
// If the upgrade was a no op, then do not continue with the second half.
138-
globals.printStatus('Flutter is already up to date on channel ${flutterVersion.channel}');
139-
globals.printStatus('$flutterVersion');
140-
} else if (!testFlow) {
140+
await attemptReset(flutterVersion, upstreamRevision);
141+
if (!testFlow) {
141142
await flutterUpgradeContinue();
142143
}
143144
}
@@ -199,23 +200,33 @@ class UpgradeCommandRunner {
199200
return false;
200201
}
201202

202-
/// Check if there is an upstream repository configured.
203+
/// Returns the remote HEAD revision.
203204
///
204205
/// Exits tool if there is no upstream.
205-
Future<void> verifyUpstreamConfigured() async {
206+
Future<String> fetchRemoteRevision() async {
207+
String revision;
206208
try {
209+
// Fetch upstream branch's commits and tags
207210
await processUtils.run(
208-
<String>[ 'git', 'rev-parse', '@{u}'],
211+
<String>['git', 'fetch', '--tags'],
209212
throwOnError: true,
210213
workingDirectory: workingDirectory,
211214
);
215+
// '@{u}' means upstream HEAD
216+
final RunResult result = await processUtils.run(
217+
<String>[ 'git', 'rev-parse', '--verify', '@{u}'],
218+
throwOnError: true,
219+
workingDirectory: workingDirectory,
220+
);
221+
revision = result.stdout.trim();
212222
} on Exception {
213223
throwToolExit(
214224
'Unable to upgrade Flutter: no origin repository configured. '
215225
"Run 'git remote add origin "
216226
"https://github.com/flutter/flutter' in $workingDirectory",
217227
);
218228
}
229+
return revision;
219230
}
220231

221232
/// Attempts to upgrade the channel.
@@ -227,33 +238,20 @@ class UpgradeCommandRunner {
227238
await ChannelCommand.upgradeChannel();
228239
}
229240

230-
/// Attempts to rebase the upstream onto the local branch.
241+
/// Attempts a hard reset to the given revision.
231242
///
232-
/// If there haven't been any hot fixes or local changes, this is equivalent
233-
/// to a fast-forward.
234-
///
235-
/// If the fast forward lands us on the same channel and revision, then
236-
/// returns true, otherwise returns false.
237-
Future<bool> attemptFastForward(FlutterVersion oldFlutterVersion) async {
238-
final int code = await processUtils.stream(
239-
<String>['git', 'pull', '--ff-only'],
243+
/// This is a reset instead of fast forward because if we are on a release
244+
/// branch with cherry picks, there may not be a direct fast-forward route
245+
/// to the next release.
246+
Future<void> attemptReset(FlutterVersion oldFlutterVersion, String newRevision) async {
247+
final RunResult result = await processUtils.run(
248+
<String>['git', 'reset', '--hard', newRevision],
249+
throwOnError: true,
240250
workingDirectory: workingDirectory,
241-
mapFunction: (String line) => matchesGitLine(line) ? null : line,
242251
);
243-
if (code != 0) {
244-
throwToolExit(null, exitCode: code);
245-
}
246-
247-
// Check if the upgrade did anything.
248-
bool alreadyUpToDate = false;
249-
try {
250-
final FlutterVersion newFlutterVersion = FlutterVersion(const SystemClock(), workingDirectory);
251-
alreadyUpToDate = newFlutterVersion.channel == oldFlutterVersion.channel &&
252-
newFlutterVersion.frameworkRevision == oldFlutterVersion.frameworkRevision;
253-
} on Exception catch (e) {
254-
globals.printTrace('Failed to determine FlutterVersion after upgrade fast-forward: $e');
252+
if (result.exitCode != 0) {
253+
throwToolExit(null, exitCode: result.exitCode);
255254
}
256-
return alreadyUpToDate;
257255
}
258256

259257
/// Update the engine repository and precache all artifacts.
@@ -300,18 +298,4 @@ class UpgradeCommandRunner {
300298
allowReentrantFlutter: true,
301299
);
302300
}
303-
304-
// dev/benchmarks/complex_layout/lib/main.dart | 24 +-
305-
static final RegExp _gitDiffRegex = RegExp(r' (\S+)\s+\|\s+\d+ [+-]+');
306-
307-
// rename {packages/flutter/doc => dev/docs}/styles.html (92%)
308-
// delete mode 100644 doc/index.html
309-
// create mode 100644 dev/integration_tests/flutter_gallery/lib/gallery/demo.dart
310-
static final RegExp _gitChangedRegex = RegExp(r' (rename|delete mode|create mode) .+');
311-
312-
static bool matchesGitLine(String line) {
313-
return _gitDiffRegex.hasMatch(line)
314-
|| _gitChangedRegex.hasMatch(line)
315-
|| line == 'Fast-forward';
316-
}
317301
}

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

+21-59
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'package:flutter_tools/src/base/file_system.dart';
66
import 'package:flutter_tools/src/base/io.dart';
77

8-
import 'package:flutter_tools/src/base/os.dart';
98
import 'package:flutter_tools/src/cache.dart';
109
import 'package:flutter_tools/src/commands/upgrade.dart';
1110
import 'package:flutter_tools/src/convert.dart';
@@ -134,7 +133,10 @@ void main() {
134133
});
135134

136135
testUsingContext("Doesn't continue on known tag, dev branch, no force, already up-to-date", () async {
136+
const String revision = 'abc123';
137+
when(flutterVersion.frameworkRevision).thenReturn(revision);
137138
fakeCommandRunner.alreadyUpToDate = true;
139+
fakeCommandRunner.remoteRevision = revision;
138140
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
139141
force: false,
140142
continueFlow: false,
@@ -159,16 +161,26 @@ void main() {
159161
Platform: () => fakePlatform,
160162
});
161163

162-
testUsingContext('verifyUpstreamConfigured', () async {
163-
when(globals.processManager.run(
164-
<String>['git', 'rev-parse', '@{u}'],
164+
testUsingContext('fetchRemoteRevision', () async {
165+
const String revision = 'abc123';
166+
when(processManager.run(
167+
<String>['git', 'fetch', '--tags'],
165168
environment:anyNamed('environment'),
166169
workingDirectory: anyNamed('workingDirectory')),
167170
).thenAnswer((Invocation invocation) async {
168171
return FakeProcessResult()
169172
..exitCode = 0;
170173
});
171-
await realCommandRunner.verifyUpstreamConfigured();
174+
when(processManager.run(
175+
<String>['git', 'rev-parse', '--verify', '@{u}'],
176+
environment:anyNamed('environment'),
177+
workingDirectory: anyNamed('workingDirectory')),
178+
).thenAnswer((Invocation invocation) async {
179+
return FakeProcessResult()
180+
..exitCode = 0
181+
..stdout = revision;
182+
});
183+
expect(await realCommandRunner.fetchRemoteRevision(), revision);
172184
}, overrides: <Type, Generator>{
173185
ProcessManager: () => processManager,
174186
Platform: () => fakePlatform,
@@ -284,67 +296,17 @@ void main() {
284296
});
285297
});
286298
});
287-
288-
group('matchesGitLine', () {
289-
setUpAll(() {
290-
Cache.disableLocking();
291-
});
292-
293-
bool _match(String line) => UpgradeCommandRunner.matchesGitLine(line);
294-
295-
test('regex match', () {
296-
expect(_match(' .../flutter_gallery/lib/demo/buttons_demo.dart | 10 +--'), true);
297-
expect(_match(' dev/benchmarks/complex_layout/lib/main.dart | 24 +-'), true);
298-
299-
expect(_match(' rename {packages/flutter/doc => dev/docs}/styles.html (92%)'), true);
300-
expect(_match(' delete mode 100644 doc/index.html'), true);
301-
expect(_match(' create mode 100644 dev/integration_tests/flutter_gallery/lib/gallery/demo.dart'), true);
302-
303-
expect(_match('Fast-forward'), true);
304-
});
305-
306-
test("regex doesn't match", () {
307-
expect(_match('Updating 79cfe1e..5046107'), false);
308-
expect(_match('229 files changed, 6179 insertions(+), 3065 deletions(-)'), false);
309-
});
310-
311-
group('findProjectRoot', () {
312-
Directory tempDir;
313-
314-
setUp(() async {
315-
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_upgrade_test.');
316-
});
317-
318-
tearDown(() {
319-
tryToDelete(tempDir);
320-
});
321-
322-
testUsingContext('in project', () async {
323-
final String projectPath = await createProject(tempDir);
324-
expect(findProjectRoot(projectPath), projectPath);
325-
expect(findProjectRoot(globals.fs.path.join(projectPath, 'lib')), projectPath);
326-
327-
final String hello = globals.fs.path.join(Cache.flutterRoot, 'examples', 'hello_world');
328-
expect(findProjectRoot(hello), hello);
329-
expect(findProjectRoot(globals.fs.path.join(hello, 'lib')), hello);
330-
});
331-
332-
testUsingContext('outside project', () async {
333-
final String projectPath = await createProject(tempDir);
334-
expect(findProjectRoot(globals.fs.directory(projectPath).parent.path), null);
335-
expect(findProjectRoot(Cache.flutterRoot), null);
336-
});
337-
});
338-
});
339299
}
340300

341301
class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
342302
bool willHaveUncomittedChanges = false;
343303

344304
bool alreadyUpToDate = false;
345305

306+
String remoteRevision = '';
307+
346308
@override
347-
Future<void> verifyUpstreamConfigured() async {}
309+
Future<String> fetchRemoteRevision() async => remoteRevision;
348310

349311
@override
350312
Future<bool> hasUncomittedChanges() async => willHaveUncomittedChanges;
@@ -353,7 +315,7 @@ class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
353315
Future<void> upgradeChannel(FlutterVersion flutterVersion) async {}
354316

355317
@override
356-
Future<bool> attemptFastForward(FlutterVersion flutterVersion) async => alreadyUpToDate;
318+
Future<bool> attemptReset(FlutterVersion flutterVersion, String newRevision) async => alreadyUpToDate;
357319

358320
@override
359321
Future<void> precacheArtifacts() async {}

packages/flutter_tools/test/integration.shard/downgrade_upgrade_integration_test.dart

+10-13
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,15 @@ void main() {
5757
'git', 'config', '--system', 'core.longpaths', 'true',
5858
]);
5959

60-
print('Step 1');
61-
// Step 1. Clone the dev branch of flutter into the test directory.
60+
print('Step 1 - clone the $_kBranch of flutter into the test directory');
6261
exitCode = await processUtils.stream(<String>[
6362
'git',
6463
'clone',
6564
'https://github.com/flutter/flutter.git',
6665
], workingDirectory: parentDirectory.path, trace: true);
6766
expect(exitCode, 0);
6867

69-
print('Step 2');
70-
// Step 2. Switch to the dev branch.
68+
print('Step 2 - switch to the $_kBranch');
7169
exitCode = await processUtils.stream(<String>[
7270
'git',
7371
'checkout',
@@ -78,8 +76,7 @@ void main() {
7876
], workingDirectory: testDirectory.path, trace: true);
7977
expect(exitCode, 0);
8078

81-
print('Step 3');
82-
// Step 3. Revert to a prior version.
79+
print('Step 3 - revert back to $_kInitialVersion');
8380
exitCode = await processUtils.stream(<String>[
8481
'git',
8582
'reset',
@@ -88,9 +85,8 @@ void main() {
8885
], workingDirectory: testDirectory.path, trace: true);
8986
expect(exitCode, 0);
9087

91-
print('Step 4');
92-
// Step 4. Upgrade to the newest stable. This should update the persistent
93-
// tool state with the sha for v1.14.3
88+
print('Step 4 - upgrade to the newest $_kBranch');
89+
// This should update the persistent tool state with the sha for HEAD
9490
exitCode = await processUtils.stream(<String>[
9591
flutterBin,
9692
'upgrade',
@@ -99,8 +95,7 @@ void main() {
9995
], workingDirectory: testDirectory.path, trace: true);
10096
expect(exitCode, 0);
10197

102-
print('Step 5');
103-
// Step 5. Verify that the version is different.
98+
print('Step 5 - verify that the version is different');
10499
final RunResult versionResult = await processUtils.run(<String>[
105100
'git',
106101
'describe',
@@ -111,8 +106,9 @@ void main() {
111106
'--tags',
112107
], workingDirectory: testDirectory.path);
113108
expect(versionResult.stdout, isNot(contains(_kInitialVersion)));
109+
print('current version is ${versionResult.stdout.trim()}\ninitial was $_kInitialVersion');
114110

115-
print('Step 6');
111+
print('Step 6 - downgrade back to the initial version');
116112
// Step 6. Downgrade back to initial version.
117113
exitCode = await processUtils.stream(<String>[
118114
flutterBin,
@@ -122,7 +118,7 @@ void main() {
122118
], workingDirectory: testDirectory.path, trace: true);
123119
expect(exitCode, 0);
124120

125-
print('Step 7');
121+
print('Step 7 - verify downgraded version matches original version');
126122
// Step 7. Verify downgraded version matches original version.
127123
final RunResult oldVersionResult = await processUtils.run(<String>[
128124
'git',
@@ -134,5 +130,6 @@ void main() {
134130
'--tags',
135131
], workingDirectory: testDirectory.path);
136132
expect(oldVersionResult.stdout, contains(_kInitialVersion));
133+
print('current version is ${oldVersionResult.stdout.trim()}\ninitial was $_kInitialVersion');
137134
});
138135
}

0 commit comments

Comments
 (0)