createBackup method

Future<String?> createBackup()

Snapshots the index and working tree without modifying either.

Returns the snapshot commit sha, or null when there is nothing to back up (clean tree) or a snapshot cannot be taken (unborn HEAD, i.e. the very first commit in a repository). Never throws: a hook must still be able to run when backups are unavailable.

Implementation

Future<String?> createBackup() async {
  // `stash create` writes a commit object but leaves the index, the working
  // tree, and the stash stack untouched -- unlike `stash push`.
  final result = await _git(['stash', 'create']);

  if (result.exitCode != 0) {
    logger.detail(
      'Could not create a backup (exit ${result.exitCode}); '
      'continuing without one. Error: ${result.stderr}',
    );
    return null;
  }

  final sha = switch (result.stdout) {
    final String out => out.trim(),
    final Future<String> out => (await out).trim(),
  };

  if (sha.isEmpty) {
    logger.detail('Nothing to back up, the index and working tree are clean');
    return null;
  }

  final anchored = await _git(['update-ref', backupRef, sha]);
  if (anchored.exitCode != 0) {
    // The snapshot still exists and [restoreBackup] still works; it is only
    // unprotected from `git gc`, so keep going rather than failing the hook.
    logger.detail(
      'Failed to anchor the backup at $backupRef. '
      'Error: ${anchored.stderr}',
    );
  }

  logger.detail('Backed up the index and working tree at $sha');

  return sha;
}