Releases: MichaelSims/git-jaspr
Release list
v2.1.1-beta.1
v2.1.0
Welcome to Jaspr v2.1.0 - No longer just for git ninjas 🚫🥷
If you thought that Jaspr seemed too complicated or required too much esoteric git knowledge to use, you might want to take a second look starting with this release.
Maybe you just didn't get why you should bother with it. If so, read on!
What is it (again)?
Jaspr (Just Another Stacked Pull Request tool) is primarily a tool to manage PR "stacks". But even if you don't care one whit about stacks, Jaspr also is a huge Quality of Life tool for creating and managing PRs (with one caveat, which I will get to below).
While I could focus on the philosophy of stacked PRs, since this release is about lowering the learning curve, let's start with the everyday conveniences and work up to what's new in 2.1.0.
It makes opening PRs way easier
If you're not already a Jaspr user, you know the drill for opening a PR:
- Create a branch. Maybe you haven't yet created one (some of us may just start working directly on our local main, making commits, and worry about the branch later). If so, you have to contrive a name, create it, and push to the remote.
- Open the PR via the GitHub UI. Best practices dictate that we provide a useful PR title and body. How many times have you opened a PR with a single commit where the subject of the commit is already suitable as the PR title? Perhaps you don't tend to provide a lot of detail in your commit bodies and save that for the PR description? I would argue that the detail belongs in both places, since it's way nicer for that to be in git history where it's a
git blameaway. But doing this "right" requires you to provide that detail twice (potentially getting away with just copying/pasting it in the case of a single commit).
How does Jaspr make this easier? It makes all of the above a single command: jaspr push
Create a branch? Not needed! Jaspr automatically creates the PR branch for you based off a unique ID. (You can push from your local main, or from a topic branch you created, or from anywhere. Jaspr doesn't care about your local branch at all. Work the way you want to without having to create a branch just to satisfy GitHub!)
Opening the GH UI? Not needed! Jaspr uses the GitHub API to create and manage PRs.
Copy/paste your commit subject and body into the PR title and description? Unnecessary! Jaspr does this for you.
And when a PR is approved and its checks are green, you don't go back to the UI to merge it either: jaspr merge merges everything that's ready, and jaspr auto-merge will wait for the checks to pass and merge the moment they do.
That brings me to the one caveat I promised: Jaspr maps one commit to one PR, and there's no way to bundle several commits into a single PR. If what you're pushing is a single commit, perfect, you get a single PR and can ignore the word "stack" entirely. If it's several commits, Jaspr opens one PR per commit and stacks them so each builds on the last. Making that case pleasant is what the rest of this document is about.
It pairs nicely with TBD (Trunk Based Development) and a "curated git history" approach
What's TBD? It's an approach that eschews long-lived branches in favor of frequent merges to trunk and continuous integration. Those who favor TBD do so because it minimizes painful merges. In TBD, branches typically exist a short amount of time (just long enough to get reviewed and merged) and then go away. Jaspr facilitates this approach because it is based around the assumption that you will frequently rebase onto the latest version of your target and force push your PR branches. It pairs well with a "curated" approach to git history where you will largely refine and amend your commits as they evolve and to address feedback, rather than creating fixup commits. This keeps git history much cleaner and nicer and makes git bisect way easier and more useful to track down regressions.
How does Jaspr make this easier? Typically following TBD involves frequent rebasing and force pushing. Jaspr simplifies this via two features: jaspr rebase and jaspr push. The former is just a shortcut for rebasing your local work on top of the latest target branch in the remote. The latter automatically force pushes your commits to the appropriate PRs with no extra effort required.
In addition, Jaspr 2.1.0 unlocks a git superpower previously reserved for git ninjas: simple history editing!
History Editing with Jaspr
Have you ever looked at your commits and wish you had approached them in a different way? Perhaps it's as simple as them being in the wrong order (a really simple commit is at the top of the list, blocked by complicated work that's still in progress, when it really could be merged immediately). Perhaps you performed a simple refactor or a series of whitespace changes as part of a commit, and you wish you had split them out. Perhaps there's a typo in a comment inside a commit that is in the middle of your list.
These issues have always been addressable by git ninjas, but knowing how to do it requires fairly deep knowledge of interactive rebasing, interactive staging of changes in the index, stashing, etc. Jaspr 2.1.0 aims to make this sort of thing simple for people who don't have that knowledge nor the patience/motivation to acquire it.
To illustrate this, let's go through some common scenarios and how you would achieve them with Jaspr. Don't worry if a command isn't obvious yet, each one is explained right after. (From this point forward, I will refer to your list of commits that aren't merged as your "stack".)
Edit a commit in the middle of your stack
You realize the third-from-bottom commit needs a fix.
jaspr goto 3 # jump straight to that commit (3rd from the bottom)
# ...make your changes...
git commit --amend
jaspr top # replay the commits that were above itInsert new commits in the middle of your stack
Same idea, but you want to add work below the tip rather than amend.
jaspr down 2 # move to where the new work belongs
git commit # add one or more commits here
jaspr top # replay the rest of the stack on topExtract a refactor into a "precursor" commit (before the feature that needs it)
You have a feature commit at the top of your stack, and you want to pull a rename or refactor out of it into its own commit first, so the feature commit becomes a clean, reviewable diff on top of the groundwork.
jaspr split # explode the HEAD commit back into working-tree changes
# ...discard the changes that belong to the feature, keeping only the refactor...
git add -A
git commit # commit the refactor as the precursor
jaspr unsplit # replay the original commit on top of the precursorjaspr unsplit replays the original commit on top of your precursor, resolving any overlaps by choosing the original's version, and keeps its message and other properties.
Rather than "carving away" the changes you don't want in the precursor, you can also just interactively stage them with git add -p. The end result is the same.
Navigate your stack
The above scenarios hint at a larger feature this release contains, which is a simple set of commands to navigate the stack. When used, Jaspr opens a lightweight navigation session that moves a detached HEAD up and down your stack. After each move, a compact banner shows where you are, dimming the commits above the cursor so the position is obvious at a glance.
| Command | What it does |
|---|---|
jaspr down [N] |
Move down N commits (toward the target branch). Default 1. |
jaspr up [N] |
Move up N commits (replaying toward the tip). Default 1. |
jaspr bottom |
Move to the bottom of the stack. |
jaspr top |
Move to the top, replaying everything remaining. |
jaspr goto N |
Jump to position N (1 = bottom, -1 = top, -2 = second from top). |
jaspr drop [N] |
Discard the top N commits. Default 1. |
Navigation sessions clean up after themselves:
jaspr nav finishends the session, keeping the commits below the cursor.jaspr nav cancel(also spelledjaspr nav abort) backs out and restores the branch you started on. If it finds leftover state from a session that already ended, it clears that for you.
Edit mid-stack commits
jaspr splitbreaks the HEAD commit back into uncommitted changes so you can re-commit it as multiple smaller commits.jaspr unsplitrestores the original commit. It picks its approach automatically based on whether HEAD has moved since the split:- Absorb (HEAD still at the split point): folds all changes back into the original commit.
- Replay on top (HEAD has moved): restores the original commit on top of the work you committed, preserving its message and identity. Content conflicts are auto-resolved in the original commit's favor. (This is the "extract a precursor" workflow.)
- Note: A rarer structural conflict (a file deleted, renamed, or type-changed on one side) can't be auto-resolved, so Jaspr pauses with the cherry-pick in progress for you to sort out by hand.
jaspr fold [up|down]squashes the current commit into its neighbor, keeping the neighbor's identity.jaspr fixupcreates afixup!commit targeting any commit in your stack, chosen interactively (via fzf when available). No SHA hunting.jaspr drop [N]discards the top N commits.jaspr rebasenow folds in any pendingfixup!commits automatically (autosquash is on by default).jaspr editopens the interactive rebase with ...