Git Triangular Workflow - @{push} branches

tl;dr if you use Pull Requests and you're not using @{push}, then you're probably missing out.

The Problem

If you use GitHub (or any other git hosting solution) to make Pull Requests, you probably have a workflow that looks something like this:

ActionCommand
Check out a new branchgit checkout -b my-feature origin/main
Make changes, run tests-
Commit changesgit commit
Push to your fork remotegit push -u fork
Raise a PR in GitHub-
Rebase your PR branch as neededgit pull --rebase origin/main
git push --force-with-lease
PR is merged in GitHub-
Delete the remote and local branchesgit push -d fork my-feature
git branch -d my-feature

If you've used this for a while, you might notice there's a bit of a problem here, in that there are really two different upstream branches you care about for a Pull Request branch:

  1. The Base Branch: the branch you want to pull or rebase from, and eventually merge into (origin/main).
  2. The PR Branch: the branch you want to push to (fork/my-feature).

By setting @{upstream} to the branch you want to push to, you lose the ability to easily have the branch also track the branch it will end up being merged into.

The Solution

Luckily git has a built-in solution for this:

  1. Use @{upstream} for the Base Branch (origin/main).
  2. Use @{push} for the PR Branch (fork/my-feature).

I discovered these via Magit, which has a great docs section on The Two Remotes.

This enables some really neat workflow optimizations (note I don't have to ever specify a remote in the normal course of affairs, which also means that I won't accidentally push to the wrong remote).

My Workflow

Commands used through a PR's lifetime:

VANILLA WORKFLOW

MY WORKFLOW

git switch -c my-feature -t origin/maing sw my-feature
make changesmake changes
git commit -ag ca
git push -u fork
then raise PR in GitHub UI
g ppr
(time passes, need to rebase)(time passes, need to rebase)
git fetch --all(automatic on cd)
git rebase origin/maing rb
git push --force-with-lease forkg pf
merge PR in the UIg prm
git branch --delete my-feature(auto-pruned by g prm)
git push --delete fork my-feature(auto-deleted by g prm)

Configuration

You probably want to set the default push location for branches to your fork remote. I call my remotes origin and fork, but you may use upstream and origin or similar. If you use different remote names, change the below commands to match.

Git Pull

When you check out a Pull Request branch, set the upstream to the branch you're going to merge into:

git checkout -b my-feature origin/main

Now things like git pull --rebase will just work, as @{upstream} is set to the right branch.

Git Push

Add this to your Git Config (git config --global --edit). This will cause git push to always default to pushing to a branch with the same name on your fork remote.

# Push to the fork remote unless a pushRemote is specified.
[remote]
  pushDefault = fork

# Only push the branch I'm on
# Made less dangerous by remote.pushDefault.
[push]
  default = current

If you want to override the push remote for a single branch, you can set that for that branch:

# Make branch my-upstream-pusher push to origin/my-upstream-pusher
# rather than fork/my-upstream-pusher.
git config branch.my-upstream-pusher.pushRemote origin

This section is optional, but will make things much easier. It also makes things safer as you will now never accidentally push to the upstream tracking branch by mistake.

Git Rebase

You can now rebase on both the @{upstream} and the @{push} branch as you wish. Note that git rebase defaults to rebasing on the @{upstream}, which is normally what you want for PRs.

Git Status

This is not currently built-in, see this StackOverflow answer to integrate it as a custom alias, and to use it in your shell prompt.

 git s  # Alias for `git status` that also shows push status.
On branch my-feature
Your branch and 'origin/main' have diverged,
and have 1 and 3 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

nothing to commit, working tree clean
Your branch is 2 commit(s) ahead and 1 commit(s) behind
    push branch fork/my-feature.

This makes it easy to see that this branch needs to be pulled and rebased from @{upstream}, and also pushed to @{push}.

Prune merged branches

You can delete the remote branches in the GitHub/GitLab UI. If you use gh pr merge --delete-branch this is done for you.

See this StackOverflow answer for deleting the local branches.

Other commands

You can use @{upstream} and @{push} anywhere you would normally use a git ref, for example:

# Undo all changes since you last pushed your PR branch.
git reset --hard @{push}

# Checkout a file as it is in the upstream branch.
git restore -s @{u} path/to/file

Optimising

There are plenty of optimizations to be done here, see my git config for my current settings.


Last updated: 2026-03-06