A command-line tool for pushing commits to GitHub as stacks of dependent pull requests. Currently in beta state: useful, but incomplete.
Let's say you have three changes you want to make to your repo's main
branch that can each stand alone, but affect the
same code. There are three ways you can deal with this in GitHub:
- Create three unrelated PRs targeting
main
, get them reviewed separately, and merge them in. This can result in merge conflicts that require extra cycles of work and attention. Worse still, these may be semantic merge conflicts that don't conflict in git, but cause compilation failures or other breaks after merging. This also may not be an option depending on how the changes are related -- for example, the first change may refactor code to create a method that the second change needs. - Put the three changes in the same PR. This results in a larger PR that is harder to review. This can lower the quality of the code review, and it can also prevent one logical part of the change from merging if a different part is rejected or delayed.
- Create three PRs, with the later changes using the earlier changes as target branches. When the first PR is merged
and its branch is deleted, the second PR's target branch will automatically switch to
main
. Depending on how CI is configured, this may not require CI to be rerun, so the three PRs can be merged in quick succession when ready. The downside here is the extra work needed to make this happen: Naming extra branches, setting the target branches when making PRs, and pushing in ways that keep branches up-to-date.
The ghpush
CLI makes the third option easy. A single command handles all the necessary branch management and PR
configuration work.
You can make the changes you want on any local branch. Make one commit per PR you want to make. Run ghpush
, and it
will guide you through adding gh-branch:
trailers to each commit. Then, it will push your changes to GitHub and create a
PR for each change.
When you want to modify commits on this branch, the recommended approach is an interactive rebase (git rebase -i
).
Modify the commits as desired, and when the rebase is done, rerun ghpush
to update all the PRs at once.
These commit message trailers are used so that when you modify a commit with git commit --amend
or git rebase -i
,
ghpush
will recognize the commit and modify the existing PR instead of creating a new one. The value of the trailer is the
branch name that will be used on GitHub. It doesn't have to be a local branch name.
You can add these to your commit messages manually, but you don't have to. Running ghpush
on an unmarked commit will
prompt you for a gh-branch value for any commit that lacks one. Typing a
at this prompt will autogenerate a branch
name based on the commit title.
See also the ghpush.prefix
config option.
ghpush
automatically creates these local branches, which you should not check out or modify. These are used to detect
if a branch has changed upstream since the last time you pushed to it with ghpush
. This is to protect you from
inadvertently deleting someone else's changes to your PRs. If this happens, ghpush
will abort, note which commits were
modified, and give further instructions.
These branches can be deleted once their associated PR is merged or closed. You can clean up these old branches by
running ghpush gc
.
Argument | Effect |
---|---|
-h or --help |
Display a help message. |
-v or --version |
Display the current version of ghpush . |
-f or --force |
Push your changes even if the branches have been updated upstream since you last pushed. |
--draft |
Push any new PRs as draft PRs. Does not modify existing PRs. |
--onto=some-release-branch |
Push your changes onto the named release branch instead of the repo's default branch. |
The push command is implied when no command is given. Explicit command options are:
Command | Effect |
---|---|
ghpush push |
The default behavior. |
ghpush gc |
Clean up old ghpush/pushed-to branches for which the PRs have merged or closed. |
ghpush
piggybacks on git config
for its configuration options. This means you can use all of git config
's commands
to configure these options either globally or in specific repos. Run git help config
for more information on how to
use it.
Quick example:
# Use your GitHub username as your branch name prefix across all repos
git config --global --add ghpush.prefix username
# Create all PRs as drafts in the current repo
git config --add ghpush.draft true
ghpush.draft
: Iftrue
, all new PRs are created as draft PRs.ghpush.prefix
: Adds a prefix togh-branch
names created usingghpush
. This can be helpful to, e.g., prefix all branches you make with a particular ID to avoid conflicts with other users' branch names. For example, ifghpush.prefix
isfoo
, and you type inbar
as the branch name, the commit will end up withgh-branch: foo/bar
. There are two special values for this feature:email
: If this is chosen, the prefix will be the first part (before the@
) of your email address as defined byuser.email
in the git config.username
: If this is chosen, the prefix will be your GitHub username, as reported bygh
.
To use ghpush
, you must have both git
and gh
(the GitHub command line tool) installed. If you install via
Homebrew, that should install gh
automatically.
brew tap AlexLandau/ghpush
brew install ghpush
MacOS/Linux: From the releases page, download the ghpush-linux
or
ghpush-macos
binary as appropriate, move it to the filename /usr/local/bin/ghpush
(may require sudo
), and run
chmod 755 /usr/local/bin/ghpush
. This location should automatically be found on your PATH. Verify that it worked by
running ghpush --version
.
Download a .tar or .zip with the latest release from the releases page,
unzip it to a directory of your choice, and then add the created ghpush/bin directory to your PATH. Verify that it
worked by running ghpush --version
.
Clone the repository, run ./gradlew install
to build, and use ./build/install/ghpush/bin/ghpush
as the binary. Don't
forget to rerun ./gradlew install
after making any code changes.
On Windows (if not using WSL), use ./gradlew.bat install
and ./build/install/ghpush/bin/ghpush.bat
instead.
Currently, ghpush requires you to have one commit per PR. If someone else makes an addition to a PR, you will have to squash that change into your commit if you want to include it in a subsequent push. Note that this can result in losing authorship information.
TODO: Fill in here
TODO: Fill in here