Skip to content

Conversation

@notactuallytreyanastasio
Copy link

@notactuallytreyanastasio notactuallytreyanastasio commented Dec 12, 2025

Add --exec option to run commands after each patch during rebases

Disclaimer

I wrote this pull request with the assistance of Claude Opus 4.5.
I can note this in the commit log if that is a desirable flag.

I am an experienced programmer and do not think this is slop, I have tried to actually implement this in a reasonable/sane manner.

I used a tool I made called deciduous while writing this and it provides a neat flow diagram of how it made the choices that are implemented, so I am including that just for fun.

Screenshot 2025-12-11 at 7 32 21 PM

The PR

This PR implements the --exec option for stg rebase, as requested in #469. It allows running a shell command after each patch is successfully applied during a rebase operation, similar to git rebase --exec.

Key features:

  • Execute shell commands after each patch push during rebase
  • Support multiple --exec options (run in sequence)
  • Commands run in user's $SHELL (or sh as fallback)
  • Graceful handling of command failures with transaction rollback

Implementation Approach

Design Decision: Modular Architecture

We considered two approaches:

  1. Handle --exec in rebase.rs only - simpler, more localized
  2. Add exec callback to transaction/push_patches - more modular, reusable

We chose Option 2 (modular approach) because:

  • It keeps the transaction logic cohesive
  • The push_patches_with_exec method could be reused by other commands in the future
  • It follows the existing pattern in the codebase where transaction operations are encapsulated

Code Changes

  1. src/stupid/context.rs: Added exec_cmd() method to run shell commands via the user's $SHELL
  2. src/stack/transaction/mod.rs: Added push_patches_with_exec() method that pushes patches and runs exec commands after each successful push
  3. src/stack/transaction/ui.rs: Added print_exec() method for user feedback
  4. src/cmd/rebase.rs: Added --exec / -x argument with appropriate conflicts

Design Discussion Point: Failure Behavior

When an exec command fails, the entire transaction is rolled back (no patches remain applied). This differs from git rebase --exec which leaves you at the failing commit to fix things.

Current behavior (rollback):

$ stg rebase --exec "make test" master
+ patch1
Executing: make test
error: `make test` exited with code 1
$ stg series
- patch1   # Rolled back to unapplied
- patch2

Git's behavior (partial state):

$ git rebase --exec "make test" master
Executing: make test
error: ...
# You're left at patch1 with a dirty worktree

Rationale for rollback:

  • Consistent with how stgit transactions work (atomic operations)
  • Safer - no partial/dirty state left for the user to clean up
  • User can always stg push patches one at a time with manual checks

However, you / the community may prefer git's behavior for consistency. I'm happy to discuss and modify this if desired.

Personally, I have never wanted to keep the mid-run state that comes with an issue when running this command, so I just went with my own personal preference here. It would not be hard to change, let me know what you think @jpgrayson @fbenkstein

Usage Examples

# Run tests after each patch
stg rebase --exec "cargo test" master

# Multiple commands
stg rebase --exec "cargo fmt --check" --exec "cargo test" master

# Complex shell commands work
stg rebase --exec "make && make test" master

Testing

  • Added comprehensive test suite in t/t2206-rebase-exec.sh
  • Tests cover: basic exec, multiple exec, failure rollback, conflict with --nopush/--interactive
  • All 7 tests pass
  • Manual testing performed using the feature itself during development

Manual Test Output

$ stg rebase --exec "echo First" --exec "echo Second" HEAD
- patch1..patch2
info: Rebasing to ...
+ patch1
Executing: echo First
First
Executing: echo Second
Second
+ patch2
Executing: echo First
First
Executing: echo Second
Second
> patch2

Implements: #469

Add support for `stg rebase --exec <cmd>` which executes a shell command
after each patch is successfully applied during the rebase operation.
This is modeled after `git rebase --exec`.

Key changes:
- Add `exec_cmd()` method to StupidContext for running shell commands
- Add `push_patches_with_exec()` method to StackTransaction for pushing
  patches with exec commands between each push
- Add `print_exec()` method to TransactionUserInterface for output
- Add `--exec` / `-x` argument to the rebase command

The exec command is run via the user's shell ($SHELL or "sh" as fallback).
Multiple --exec options can be specified to run multiple commands in
sequence after each patch. If any command fails, the rebase halts.

Note: When an exec command fails, the entire transaction is rolled back
(no patches remain applied). This differs from git rebase --exec which
leaves you at the failing point. The rollback behavior is safer and
consistent with how stgit transactions work.

Implements: stacked-git#469
@notactuallytreyanastasio notactuallytreyanastasio changed the title feat(rebase): add --exec option to run commands after each patch Feature Request: add --exec option to run commands after each patch during rebase Dec 12, 2025
@notactuallytreyanastasio notactuallytreyanastasio changed the title Feature Request: add --exec option to run commands after each patch during rebase Feature Request Implementation: add --exec option to run commands after each patch during rebase Dec 12, 2025
@notactuallytreyanastasio notactuallytreyanastasio changed the title Feature Request Implementation: add --exec option to run commands after each patch during rebase feat: add --exec option to run commands after each patch during rebase Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant