Skip to content

[Feature]: Add a gitbun undo command that reverts the last commit created by Gitbun while preserving staged changes, with a confirmation prompt #76

Description

@divyanshim27

Summary

Gitbun's --auto flag commits immediately without preview. Even in interactive mode, a user can accidentally confirm and commit a generated message they didn't fully review. There is currently no recovery path — the user must manually run git reset HEAD~1 to undo the last commit. A gitbun undo command that wraps this operation with a safety confirmation and preserves staged changes would complete Gitbun's workflow loop.

Problem

  • When --auto is used in CI or by habit, a bad commit message cannot be corrected without knowing the git command to undo it.
  • git reset HEAD~1 --soft is not intuitive for newer developers — Gitbun's entire purpose is to abstract git workflow complexity.
  • There is no way inside Gitbun to regenerate a commit message after committing — the user must amend manually.
  • Gitbun tracks no metadata about commits it has created, so there's no way to verify what the "last Gitbun commit" was before undoing it.

Proposed Solution

1. Record Gitbun-created commits in a local metadata file:

// src/lib/commit-log.ts
import { join } from 'path';
import { readFileSync, writeFileSync, existsSync } from 'fs';

const GIT_DIR = join(process.cwd(), '.git');
const GITBUN_LOG = join(GIT_DIR, 'GITBUN_LAST_COMMIT');

export function recordCommit(sha: string, message: string) {
  writeFileSync(GITBUN_LOG, JSON.stringify({ sha, message, timestamp: Date.now() }));
}

export function getLastGitbunCommit(): { sha: string; message: string; timestamp: number } | null {
  if (!existsSync(GITBUN_LOG)) return null;
  try {
    return JSON.parse(readFileSync(GITBUN_LOG, 'utf-8'));
  } catch {
    return null;
  }
}

2. Add the undo subcommand:

// src/commands/undo.ts
import { execSync } from 'child_process';
import { getLastGitbunCommit } from '../lib/commit-log.js';
import chalk from 'chalk';
import inquirer from 'inquirer';

export async function undoLastCommit() {
  const lastCommit = getLastGitbunCommit();

  if (!lastCommit) {
    console.log(chalk.yellow('No Gitbun commit found to undo.'));
    return;
  }

  // Verify the last commit SHA still matches HEAD
  const headSha = execSync('git rev-parse HEAD').toString().trim();
  if (headSha !== lastCommit.sha) {
    console.log(chalk.red(
      `The last Gitbun commit (${lastCommit.sha.slice(0, 7)}) is no longer HEAD.\n` +
      `Cannot safely undo — run \`git reset HEAD~1 --soft\` manually if needed.`
    ));
    return;
  }

  console.log(chalk.cyan('Last Gitbun commit:'));
  console.log(chalk.white(`  ${lastCommit.message}`));
  console.log(chalk.gray(`  ${new Date(lastCommit.timestamp).toLocaleString()}`));
  console.log('');

  const { confirmed } = await inquirer.prompt([{
    type: 'confirm',
    name: 'confirmed',
    message: 'Undo this commit? (staged changes will be preserved)',
    default: false,
  }]);

  if (!confirmed) {
    console.log(chalk.gray('Undo cancelled.'));
    return;
  }

  execSync('git reset HEAD~1 --soft');
  console.log(chalk.green('✓ Commit undone. Your changes are still staged.'));
  console.log(chalk.cyan('Run `gitbun` to generate a new commit message.'));
}

3. Register the command in the CLI entry point:

// bin/gitbun.js (or src/index.ts)
program
  .command('undo')
  .description('Undo the last commit created by Gitbun (preserves staged changes)')
  .action(undoLastCommit);

Additional Notes

  • git reset HEAD~1 --soft is used (not --hard) — no changes are lost. All files return to the staged state.
  • The GITBUN_LAST_COMMIT file is stored inside .git/, which means it is gitignored by default and never committed.
  • If the last commit at HEAD is not a Gitbun commit (e.g., the user made a manual commit after), the undo is blocked with a clear error message.
  • I will add a unit test for commit-log.ts (record and retrieve) and a CLI integration test for the undo command flow.

Could you assign this issue to me?

Labels: enhancement, feature, cli, GSSoC 2026

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions