blob: d1a3373d83416175078d120c6abec8bca3806acb [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'base/file_system.dart';
import 'base/logger.dart';
import 'flutter_project_metadata.dart';
import 'utils.dart';
/// Handles the custom/manual merging of one file at `localPath`.
///
/// The `merge` method should be overridden to implement custom merging.
abstract class CustomMerge {
CustomMerge({
required this.logger,
required this.localPath,
});
/// The local path (with the project root as the root directory) of the file to merge.
final String localPath;
final Logger logger;
/// Called to perform a custom three way merge between the current,
/// base, and target files.
MergeResult merge(File current, File base, File target);
}
/// Manually merges a flutter .metadata file.
///
/// See `FlutterProjectMetadata`.
class MetadataCustomMerge extends CustomMerge {
MetadataCustomMerge({
required super.logger,
}) : super(localPath: '.metadata');
@override
MergeResult merge(File current, File base, File target) {
final FlutterProjectMetadata result = computeMerge(
FlutterProjectMetadata(current, logger),
FlutterProjectMetadata(base, logger),
FlutterProjectMetadata(target, logger),
logger,
);
return StringMergeResult.explicit(
mergedString: result.toString(),
hasConflict: false,
exitCode: 0,
localPath: localPath,
);
}
FlutterProjectMetadata computeMerge(
FlutterProjectMetadata current,
FlutterProjectMetadata base,
FlutterProjectMetadata target,
Logger logger) {
// Prefer to update the version revision and channel to latest version.
final String? versionRevision = target.versionRevision ??
current.versionRevision ??
base.versionRevision;
final String? versionChannel =
target.versionChannel ?? current.versionChannel ?? base.versionChannel;
// Prefer to leave the project type untouched as it is non-trivial to change project type.
final FlutterProjectType? projectType =
current.projectType ?? base.projectType ?? target.projectType;
final MigrateConfig migrateConfig = mergeMigrateConfig(
current.migrateConfig,
target.migrateConfig,
);
final FlutterProjectMetadata output = FlutterProjectMetadata.explicit(
file: current.file,
versionRevision: versionRevision,
versionChannel: versionChannel,
projectType: projectType,
migrateConfig: migrateConfig,
logger: logger,
);
return output;
}
MigrateConfig mergeMigrateConfig(
MigrateConfig current, MigrateConfig target) {
// Create the superset of current and target platforms with baseRevision updated to be that of target.
final Map<FlutterProjectComponent, MigratePlatformConfig>
projectComponentConfigs =
<FlutterProjectComponent, MigratePlatformConfig>{};
for (final MapEntry<FlutterProjectComponent, MigratePlatformConfig> entry
in current.platformConfigs.entries) {
if (target.platformConfigs.containsKey(entry.key)) {
projectComponentConfigs[entry.key] = MigratePlatformConfig(
component: entry.value.component,
createRevision: entry.value.createRevision,
baseRevision: target.platformConfigs[entry.key]?.baseRevision);
} else {
projectComponentConfigs[entry.key] = entry.value;
}
}
for (final MapEntry<FlutterProjectComponent, MigratePlatformConfig> entry
in target.platformConfigs.entries) {
if (!projectComponentConfigs.containsKey(entry.key)) {
projectComponentConfigs[entry.key] = entry.value;
}
}
// Ignore the base file list.
final List<String> unmanagedFiles =
List<String>.from(current.unmanagedFiles);
for (final String path in target.unmanagedFiles) {
if (!unmanagedFiles.contains(path) &&
!MigrateConfig.kDefaultUnmanagedFiles.contains(path)) {
unmanagedFiles.add(path);
}
}
return MigrateConfig(
platformConfigs: projectComponentConfigs,
unmanagedFiles: unmanagedFiles,
);
}
}