Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 0 additions & 50 deletions dev/conductor/core/lib/src/repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -385,50 +385,6 @@ abstract class Repository {
return exitcode == 0;
}

/// Determines if a commit will cherry-pick to current HEAD without conflict.
Future<bool> canCherryPick(String commit) async {
assert(
await gitCheckoutClean(),
'cannot cherry-pick because git checkout ${(await checkoutDirectory).path} is not clean',
);

final int exitcode = await git.run(
<String>['cherry-pick', '--no-commit', commit],
'attempt to cherry-pick $commit without committing',
allowNonZeroExitCode: true,
workingDirectory: (await checkoutDirectory).path,
);

final bool result = exitcode == 0;

if (result == false) {
stdio.printError(await git.getOutput(
<String>['diff'],
'get diff of failed cherry-pick',
workingDirectory: (await checkoutDirectory).path,
));
}

await reset('HEAD');
return result;
}

/// Cherry-pick a [commit] to the current HEAD.
///
/// This method will throw a [GitException] if the command fails.
Future<void> cherryPick(String commit) async {
assert(
await gitCheckoutClean(),
'cannot cherry-pick because git checkout ${(await checkoutDirectory).path} is not clean',
);

await git.run(
<String>['cherry-pick', commit],
'cherry-pick $commit',
workingDirectory: (await checkoutDirectory).path,
);
}

/// Resets repository HEAD to [ref].
Future<void> reset(String ref) async {
await git.run(
Expand Down Expand Up @@ -792,12 +748,6 @@ class HostFrameworkRepository extends FrameworkRepository {
'checkout not implemented for the host repository');
}

@override
Future<String> cherryPick(String commit) async {
throw ConductorException(
'cherryPick not implemented for the host repository');
}

@override
Future<String> reset(String ref) async {
throw ConductorException('reset not implemented for the host repository');
Expand Down
151 changes: 1 addition & 150 deletions dev/conductor/core/lib/src/start.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ import 'version.dart';

const String kCandidateOption = 'candidate-branch';
const String kDartRevisionOption = 'dart-revision';
const String kEngineCherrypicksOption = 'engine-cherrypicks';
const String kEngineUpstreamOption = 'engine-upstream';
const String kFrameworkCherrypicksOption = 'framework-cherrypicks';
const String kFrameworkMirrorOption = 'framework-mirror';
const String kFrameworkUpstreamOption = 'framework-upstream';
const String kEngineMirrorOption = 'engine-mirror';
Expand Down Expand Up @@ -70,16 +68,6 @@ class StartCommand extends Command<void> {
defaultsTo: defaultPath,
help: 'Path to persistent state file. Defaults to $defaultPath',
);
argParser.addMultiOption(
kEngineCherrypicksOption,
help: 'Engine cherrypick hashes to be applied.',
defaultsTo: <String>[],
);
argParser.addMultiOption(
kFrameworkCherrypicksOption,
help: 'Framework cherrypick hashes to be applied.',
defaultsTo: <String>[],
);
argParser.addOption(
kDartRevisionOption,
help: 'New Dart revision to cherrypick.',
Expand Down Expand Up @@ -151,16 +139,6 @@ class StartCommand extends Command<void> {
argumentResults,
platform.environment,
)!;
final List<String> frameworkCherrypickRevisions = getValuesFromEnvOrArgs(
kFrameworkCherrypicksOption,
argumentResults,
platform.environment,
);
final List<String> engineCherrypickRevisions = getValuesFromEnvOrArgs(
kEngineCherrypicksOption,
argumentResults,
platform.environment,
);
final String? dartRevision = getValueFromEnvOrArgs(
kDartRevisionOption,
argumentResults,
Expand Down Expand Up @@ -191,11 +169,9 @@ class StartCommand extends Command<void> {
candidateBranch: candidateBranch,
checkouts: checkouts,
dartRevision: dartRevision,
engineCherrypickRevisions: engineCherrypickRevisions,
engineMirror: engineMirror,
engineUpstream: engineUpstream,
conductorVersion: conductorVersion,
frameworkCherrypickRevisions: frameworkCherrypickRevisions,
frameworkMirror: frameworkMirror,
frameworkUpstream: frameworkUpstream,
processManager: processManager,
Expand All @@ -216,10 +192,8 @@ class StartContext extends Context {
StartContext({
required this.candidateBranch,
required this.dartRevision,
required this.engineCherrypickRevisions,
required this.engineMirror,
required this.engineUpstream,
required this.frameworkCherrypickRevisions,
required this.frameworkMirror,
required this.frameworkUpstream,
required this.conductorVersion,
Expand Down Expand Up @@ -258,10 +232,8 @@ class StartContext extends Context {

final String candidateBranch;
final String? dartRevision;
final List<String> engineCherrypickRevisions;
final String engineMirror;
final String engineUpstream;
final List<String> frameworkCherrypickRevisions;
final String frameworkMirror;
final String frameworkUpstream;
final String conductorVersion;
Expand Down Expand Up @@ -326,70 +298,20 @@ class StartContext extends Context {
await engine.updateDartRevision(dartRevision!);
await engine.commit('Update Dart SDK to $dartRevision', addFirst: true);
}
final List<pb.Cherrypick> engineCherrypicks = (await _sortCherrypicks(
repository: engine,
cherrypicks: engineCherrypickRevisions,
upstreamRef: EngineRepository.defaultBranch,
releaseRef: candidateBranch,
))
.map((String revision) => pb.Cherrypick(
trunkRevision: revision,
state: pb.CherrypickState.PENDING,
))
.toList();

for (final pb.Cherrypick cherrypick in engineCherrypicks) {
final String revision = cherrypick.trunkRevision;
final bool success = await engine.canCherryPick(revision);
stdio.printTrace(
'Attempt to cherrypick $revision ${success ? 'succeeded' : 'failed'}',
);
if (success) {
await engine.cherryPick(revision);
cherrypick.state = pb.CherrypickState.COMPLETED;
} else {
cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT;
}
}

final String engineHead = await engine.reverseParse('HEAD');
state.engine = pb.Repository(
candidateBranch: candidateBranch,
workingBranch: workingBranchName,
startingGitHead: engineHead,
currentGitHead: engineHead,
checkoutPath: (await engine.checkoutDirectory).path,
cherrypicks: engineCherrypicks,
dartRevision: dartRevision,
upstream: pb.Remote(name: 'upstream', url: engine.upstreamRemote.url),
mirror: pb.Remote(name: 'mirror', url: engine.mirrorRemote!.url),
);

await framework.newBranch(workingBranchName);
final List<pb.Cherrypick> frameworkCherrypicks = (await _sortCherrypicks(
repository: framework,
cherrypicks: frameworkCherrypickRevisions,
upstreamRef: FrameworkRepository.defaultBranch,
releaseRef: candidateBranch,
))
.map((String revision) => pb.Cherrypick(
trunkRevision: revision,
state: pb.CherrypickState.PENDING,
))
.toList();

for (final pb.Cherrypick cherrypick in frameworkCherrypicks) {
final String revision = cherrypick.trunkRevision;
final bool success = await framework.canCherryPick(revision);
stdio.printTrace(
'Attempt to cherrypick $cherrypick ${success ? 'succeeded' : 'failed'}',
);
if (success) {
await framework.cherryPick(revision);
cherrypick.state = pb.CherrypickState.COMPLETED;
} else {
cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT;
}
}

// Get framework version
final Version lastVersion = Version.fromString(await framework.getFullTag(
Expand Down Expand Up @@ -436,7 +358,6 @@ class StartContext extends Context {
startingGitHead: frameworkHead,
currentGitHead: frameworkHead,
checkoutPath: (await framework.checkoutDirectory).path,
cherrypicks: frameworkCherrypicks,
upstream: pb.Remote(name: 'upstream', url: framework.upstreamRemote.url),
mirror: pb.Remote(name: 'mirror', url: framework.mirrorRemote!.url),
);
Expand Down Expand Up @@ -520,74 +441,4 @@ class StartContext extends Context {
stdio.printStatus('The actual release will be version $nextVersion.');
return nextVersion;
}

// To minimize merge conflicts, sort the commits by rev-list order.
Future<List<String>> _sortCherrypicks({
required Repository repository,
required List<String> cherrypicks,
required String upstreamRef,
required String releaseRef,
}) async {
if (cherrypicks.isEmpty) {
return cherrypicks;
}

// Input cherrypick hashes that failed to be parsed by git.
final List<String> unknownCherrypicks = <String>[];
// Full 40-char hashes parsed by git.
final List<String> validatedCherrypicks = <String>[];
// Final, validated, sorted list of cherrypicks to be applied.
final List<String> sortedCherrypicks = <String>[];
for (final String cherrypick in cherrypicks) {
try {
final String fullRef = await repository.reverseParse(cherrypick);
validatedCherrypicks.add(fullRef);
} on GitException {
// Catch this exception so that we can validate the rest.
unknownCherrypicks.add(cherrypick);
}
}

final String branchPoint = await repository.branchPoint(
'${repository.upstreamRemote.name}/$upstreamRef',
'${repository.upstreamRemote.name}/$releaseRef',
);

// `git rev-list` returns newest first, so reverse this list
final List<String> upstreamRevlist = (await repository.revList(<String>[
'--ancestry-path',
'$branchPoint..$upstreamRef',
]))
.reversed
.toList();

stdio.printStatus('upstreamRevList:\n${upstreamRevlist.join('\n')}\n');
stdio.printStatus(
'validatedCherrypicks:\n${validatedCherrypicks.join('\n')}\n');
for (final String upstreamRevision in upstreamRevlist) {
if (validatedCherrypicks.contains(upstreamRevision)) {
validatedCherrypicks.remove(upstreamRevision);
sortedCherrypicks.add(upstreamRevision);
if (unknownCherrypicks.isEmpty && validatedCherrypicks.isEmpty) {
return sortedCherrypicks;
}
}
}

// We were given input cherrypicks that were not present in the upstream
// rev-list
stdio.printError(
'The following ${repository.name} cherrypicks were not found in the '
'upstream $upstreamRef branch:',
);
for (final String cp in <String>[
...validatedCherrypicks,
...unknownCherrypicks
]) {
stdio.printError('\t$cp');
}
throw ConductorException(
'${validatedCherrypicks.length + unknownCherrypicks.length} unknown cherrypicks provided!',
);
}
}
Loading