run method

Future<int> run()

Implementation

Future<int> run() async {
  final allFilesResult = await this.allFiles;
  if (allFilesResult case (_, final int code)) {
    return code;
  }
  final (allFiles, _) = allFilesResult;
  logger.detail('Found ${allFiles.length} files');
  for (final file in allFiles) {
    logger.detail('  - $file');
  }

  if (debug) await _wait(durations.short);

  logger.detail('Resolving files');

  final pendingHook = PendingHook(
    hook.resolve(allFiles),
    logger: logger,
  );

  if (pendingHook.topLevelTasks.every((e) => e.files.isEmpty)) {
    logger.info(
      darkGray.wrap('Skipping $hookName hook, no files match any tasks'),
    );
    return 0;
  }

  logger
    ..info(darkGray.wrap('Running $hookName hook'))
    ..detail('Preparing files');
  final context = await gitService.prepareFiles(backup: hook.backupFiles);
  if (debug) await _wait(durations.short);

  if (context.hidePartiallyStaged) {
    logger.detail(
      'Hiding partially staged files '
      '(${context.partiallyStagedFiles.length})',
    );
    await gitService.checkoutFiles(context.partiallyStagedFiles);
  }

  final labelMaker = LabelMaker(
    stdout: stdout,
    pendingHook: pendingHook,
    nameOfHook: hookName,
    debug: debug,
  );

  logger.detail('Starting tasks');
  if (debug) await _wait(durations.short);

  final progress = MultiLineProgress(createLabel: labelMaker.create)..start();

  pendingHook.start();

  await pendingHook.wait();

  if (pendingHook.wasKilled) {
    progress
      ..dispose()
      ..print();

    logger.detail('Hook was killed');
  } else {
    await progress.closeNextFrame();

    logger
      ..detail('Tasks finished')
      ..flush()
      ..write('\n');
  }

  Future<void> finish() async {
    logger.detail('deleting patch');
    await gitService.deletePatch();

    logger.detail('restoring merge statuses');
    gitService.restoreMergeStatuses(
      msg: context.mergeMsg,
      mode: context.mergeMode,
      head: context.mergeHead,
    );

    if (debug) await _wait(durations.short);

    logger.detail('dropping stash');
    await gitService.dropBackupStash();
  }

  Future<int> fail() async {
    final stash = context.stashHash;
    if (stash == null) {
      return 1;
    }

    logger.detail('Forcing hard reset to HEAD');
    await gitService.restoreStash();

    if (debug) await _wait(durations.long);

    logger.detail('making sure all deleted files stay deleted '
        '(${context.deletedFiles.length})');

    if (debug) await _wait(durations.short);
    await gitService.ensureDeletedFiles(context.deletedFiles);

    await finish();

    return 1;
  }

  var failed = false;
  for (final task in pendingHook.topLevelTasks) {
    if (task.code case final int code when code != 0) {
      failed = true;
      logger.detail(
        'Task failed: ${task.resolvedTask.original.resolvedName}',
      );
    }

    if (failed) {
      logger
        ..detail('Task failed, stopping')
        ..flush();
      return await fail();
    }
  }

  if (hook.diffArgs.isEmpty) {
    logger.detail('Applying modifications');
    if (debug) await _wait(durations.short);
    await gitService.applyModifications(context.nonStagedFiles);
    if (debug) await _wait(durations.long);
  } else {
    logger.detail('Skipping modifications due to diffArgs being set');
  }

  if (context.hidePartiallyStaged) {
    logger.detail('Restoring unstaged changes');
    if (!await gitService.applyPatch()) {
      logger.err('Failed to restore unstaged changes due to merge conflicts');
      if (debug) await _wait(durations.long);

      await fail();
    }
  }

  await finish();

  if (hook.allowEmpty) {
    logger.detail('--FINISHED--');
    return 0;
  }

  final files = await gitService.diffFiles(
    diffArgs: hook.diffArgs,
    diffFilters: hook.diffFilters,
  );

  if (files != null && files.isEmpty) {
    logger
      ..info('No changes to commit')
      ..detail('--FINISHED--');
    return 1;
  }

  logger.detail('--FINISHED--');
  return 0;
}