
A collection of components for displaying Git information in your prompt
Last Updated
A version of this article appeared on viget.com

I get into this situation sometimes. Maybe you do too. I merge feature work into a branch used to collect features, and then continue development but on that branch instead of back on the feature branch
git checkout feature
# ... bunch of feature commits ...
git push
git checkout qa-environment
git merge --no-ff --no-edit feature
git push
# deploy qa-environment to the QA remote environment
# ... more feature commits ...
# oh. I'm not committing in the feature branch like I should beand have to move those commits to the feature branch they belong in and take them out of the throwaway accumulator branch. For example
git checkout feature
git cherry-pick origin/qa-environment..qa-environment
git push
git checkout qa-environment
git reset --hard origin/qa-environment
git merge --no-ff --no-edit feature
git checkout feature
# ready for more feature commitsThat works. But it’d be nicer if we didn’t have to type or even remember the branches’ names and the remote’s name. They are what is keeping this from being a context-independent string of commands you run any time this mistake happens. That’s what we’re going to solve here.
I like to use all possible natively supported shorthands. There are two broad motivations for that.
First up for our scenario: the - shorthand, which refers to the previously checked out branch. There are a few places we can’t use it, but it helps a lot: (🎉 marks wins from -)
# USING THE "-" SHORTHAND
git checkout feature
# hack hack hack
git push
git checkout qa-environment
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit -
git push
# hack hack hack
# whoops
git checkout feature
git checkout - # now on feature
git cherry-pick origin/qa-environment..qa-environment
git push
git checkout qa-environment
git checkout - # now on qa-environment
git reset --hard origin/qa-environment
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit -
git checkout feature
git checkout -
# on feature and ready for more feature commitsThat’s as far as - gets us. We cannot use it when cherry-picking a range
% git cherry-pick origin/-..-
fatal: bad revision 'origin/-..-'
% git cherry-pick origin/qa-environment..-
fatal: bad revision 'origin/qa-environment..-'and even if we could we’d still have provide the remote’s name (here, origin).
And doesn’t apply in the later reset --hard command. And we cannot use it in the branch -D && checkout approach either, since branch -D does not support the - shorthand and once the branch is deleted checkout can’t reach it with -:
# assuming that branch-a has an upstream origin/branch-a
% git checkout branch-a
% git checkout branch-b
% git checkout -
% git branch -D -
error: branch '-' not found.
% git branch -D branch-a
% git checkout -
error: pathspec '-' did not match any file(s) known to gitSo we have to remember the remote’s name (we know it’s origin because we are devoting memory space to knowing that this isn’t one of those times it’s something else), the remote tracking branch’s name, the local branch’s name, and we’re typing those all out. No good! Let’s figure out some more shorthands.
We can do a little better by using @{-<n>} (you’ll also sometimes see it referred to as the older @{-N}). It is a special construct for referring to the nth previously checked out ref.
% git checkout branch-a
% git checkout branch-b
% git rev-parse --abbrev-rev @{-1} # the name of the previously checked out branch
branch-a
% git checkout branch-c
% git rev-parse --abbrev-rev @{-2} # the name of branch checked out before the previously checked out one
branch-aBack in our scenario, we’re on qa-environment, we switch to feature, and then want to refer to qa-environment. That’s @{-1}! So instead of
git cherry-pick origin/qa-environment..qa-environmentWe can do
git cherry-pick origin/qa-environment..@{-1}Here’s where we are
# USING THE "-" AND @{-1} SHORTHANDS
git checkout feature
# hack hack hack
git push
git checkout qa-environment
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit - # thanks to `-`
git push
# hack hack hack
# whoops
git checkout feature
git checkout - # now on feature thanks to `-`
git cherry-pick origin/qa-environment..qa-environment
git cherry-pick origin/qa-environment..@{-1} # thanks to `@{-1}`
git push
git checkout qa-environment
git checkout - # now on qa-environment thanks to `-`
git reset --hard origin/qa-environment
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit - # thanks to `-`
git checkout feature
git checkout - # thanks to `-`
# on feature and ready for more feature commitsOne down, two to go: we’re still relying on memory for the remote’s name and the remote branch’s name, and we’re still typing both out in full. Can we replace those with generic shorthands?
@{-1} is the ref itself, not the ref’s name, we can’t do
% git cherry-pick origin/@{-1}..@{-1}
origin/@{-1}
fatal: ambiguous argument 'origin/@{-1}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'because there is no branch origin/@{-1}. For the same reason, @{-1} does not give us a generalized shorthand for the scenario’s later git reset --hard origin/qa-environment command.
But good news!
@{upstream} or its shorthand @{u} is the remote branch a that would be pulled from if git pull were run. @{push} is the remote branch that would be pushed to if git push was run.
% git checkout branch-a
Switched to branch 'branch-a'
Your branch is ahead of 'origin/branch-a' by 3 commits.
(use "git push" to publish your local commits)
% git reset --hard origin/branch-a
HEAD is now at <the SHA origin/branch-a is at>we can
% git checkout branch-a
Switched to branch 'branch-a'
Your branch is ahead of 'origin/branch-a' by 3 commits.
(use "git push" to publish your local commits)
% git reset --hard @{u} # <-- So Cool!
HEAD is now at <the SHA origin/branch-a is at>Tacking either onto a branch name will give that branch’s @{upstream} or @{push}. For example
git checkout branch-a@{u}is the branch branch-a pulls from.
In the common workflow where a branch pulls from and pushes to the same branch, @{upstream} and @{push} will be the same, leaving @{u} as preferable for its terseness. @{push} shines in triangular workflows where you pull from one remote and push to another (see the external links below).
Going back to our scenario, it means short, portable commands with a minimum human memory footprint.
# USING - AND @{-1} AND @{u}
git checkout feature
# hack hack hack
git push
git checkout qa-environment
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit - # thanks to `-`
git push
# hack hack hack
# whoops
git checkout feature
git checkout - # now on feature thanks to `-`
git cherry-pick origin/qa-environment..qa-environment
git cherry-pick @{-1}@{u}..@{-1} # thanks to `@{-1}` and `@{u}`
git push
git checkout qa-environment
git checkout - # now on qa-environment thanks to `-`
git reset --hard origin/qa-environment
git reset --hard @{u} # thanks to `@{u}`
git merge --no-ff --no-edit feature
git merge --no-ff --no-edit - # thanks to `-`
git checkout feature
git checkout - # thanks to `-`
# on feature and ready for more feature commitsBecause these commands are generalized, we can run some series of them once —for example,
git checkout - && git cherry-pick @{-1}@{u}..@{-1} && git branch -f @{-1} @{-1}@{u}— and then that will be in the shell history just waiting to be retrieved and run again the next time, whether with CtrlR incremental search, or history substring searching bound to the up arrow, or however your interactive shell is configured (more: My .zshrc Zsh Configuration, Annotated). Or make it an alias, or even better an abbreviation if your interactive shell supports them (more: zsh-abbr). Save the body wear and tear, give memory a break, and level up in Git.
The GitHub blog has a good primer on triangular workflows and how they can polish your process of contributing to external projects.
The FreeBSD Wiki has a more in-depth article on triangular workflow process (though it doesn’t know about @{push} and @{upstream}).
The construct @{-<n>} and the suffixes @{push} and @{upstream} are all part of the gitrevisions spec. Direct links to each:

Git Prompt Kit: Configurable, Fast Git Status Components For Custom Zsh Themes
A collection of components for displaying Git information in your prompt

The command line (mostly Git) abbreviations I rely on
The abbreviations that have saved me countless terminal keystrokes, and the commands to add them to your zsh or fish setup

My .zshrc Zsh Configuration, Annotated
How I set up my interactive shell