detectChanges method

Future<GitChangesResult> detectChanges()

Implementation

Future<GitChangesResult> detectChanges() async {
  final gitDir = Directory(p.join(projectRoot, '.git'));
  if (!gitDir.existsSync()) {
    return GitChangesResult.noGitRepo();
  }

  try {
    final statusResult = await Process.run(
      'git',
      ['status', '--short'],
      workingDirectory: projectRoot,
    );

    if (statusResult.exitCode != 0) {
      return GitChangesResult.noGitRepo();
    }

    final lines = (statusResult.stdout as String).split('\n');
    final Set<String> allChanged = {};
    final List<String> modified = [];
    final List<String> added = [];
    final List<String> deleted = [];

    bool hasProjectConfigChange = false;
    bool hasNativeCodeChange = false;
    String? configFallbackReason;

    for (final line in lines) {
      final trimmed = line.trimRight();
      if (trimmed.isEmpty) continue;

      final statusCode = trimmed.substring(0, 2).trim();
      var filePath = trimmed.substring(2).trim();

      if (filePath.length >= 2 &&
          filePath.codeUnitAt(0) == 34 &&
          filePath.codeUnitAt(filePath.length - 1) == 34) {
        filePath = filePath.substring(1, filePath.length - 1);
      }

      final normalized = filePath.replaceAll('\\', '/');
      allChanged.add(normalized);

      if (statusCode.contains('M') || statusCode == 'MM') {
        modified.add(normalized);
      } else if (statusCode.contains('A') || statusCode == '??') {
        added.add(normalized);
      } else if (statusCode.contains('D')) {
        deleted.add(normalized);
      } else {
        modified.add(normalized);
      }

      final lower = normalized.toLowerCase();
      if (lower == 'pubspec.yaml' ||
          lower == 'pubspec.lock' ||
          lower == 'analysis_options.yaml' ||
          lower == 'build.yaml') {
        hasProjectConfigChange = true;
        configFallbackReason = 'project_configuration_changed';
      } else if (lower.startsWith('android/') ||
          lower.startsWith('ios/') ||
          lower.startsWith('macos/') ||
          lower.startsWith('windows/') ||
          lower.startsWith('linux/') ||
          lower.startsWith('web/')) {
        hasNativeCodeChange = true;
        configFallbackReason ??= 'native_code_changed';
      }
    }

    final requiresFallback = hasProjectConfigChange || hasNativeCodeChange;

    return GitChangesResult(
      changedFiles: allChanged.toList(),
      modified: modified,
      added: added,
      deleted: deleted,
      isGitRepository: true,
      requiresFallback: requiresFallback,
      fallbackReason: configFallbackReason,
    );
  } catch (e) {
    return GitChangesResult.noGitRepo();
  }
}