git pr-train
helps you manage your PR chain when you need split a long PR into smaller ones.
If you have a chain of PRs, git pr-train
:
- Makes sure all your branches in the chain get updated when you modify any of them
- Creates GitHub PRs for you with a table of contents
Doing those two things manually can be (very) tedious and frustrating, believe you me.
Install with npm i -g git-pr-train
.
Run git pr-train --init
in your repo root to generate a .pr-train.yml
file (don't forget to gitignore).
Now whenever you have a chain of branches, list them in .pr-train.yml
to tell pr-train which branches form the chain, and you're good to go.
git pr-train -p
will merge branches sequentially one into another and pushgit pr-train -r -p -f
will rebase branches instead of merging and then push with--force
.git pr-train -h
to print usage information
Pre-requisite: Create a ${HOME}/.pr-train
file with a single line which is your GH personal access token (you can create one here). The repo
scope, with Full control of private repositories
is needed.
Run git pr-train -p --create-prs
to create GitHub PRs with a "content table" section. PR titles are taken from the commit message titles of each branch HEAD. You'll be prompted before the PRs are created.
If you run with --create-prs
again, pr-train
will only override the Table of Contents in your PR, it will not change the rest of the PR descriptions.
Pro-tip: If you want to update the ToCs in your GitHub PRs, just update the PR titles and re-run pr train with --create-prs
- it will do the right thing.
To create PRs in draft mode (if your repo allows),
pass the -d
or --draft
argument on the command line (in addition to -c
/--create-prs
).
You can also configure PRs to be created in draft mode by default if you add the following section to your .pr-train.yml
file:
prs:
draft-by-default: true
trains:
# etc
Specifying this option will allow you to omit the -d
/--draft
parameter (though you still need to specify -c
/--create-prs
) when you want to create/update PRs.
You finished coding a feature and now you have a patch that is over 1000 SLOCs long. That's a big patch. As a good citizen, you want to split the diff into multiple PRs, e.g.:
fred_billing-refactor_frontend_bits
fred_billing-refactor_backend_bits
fred_billing-refactor_tests
That's what we call that a PR train.
If you modify a branch (or e.g. merge/rebase fred_billing-refactor_frontend_bits
on top of master
), you'll want all the other branches to receive the change. git pr-train
does that by merging (or rebasing) each branch into their child branch (i.e., branch 1 into branch 2, branch 2 into branch 3 etc).
If you wish, it also makes sure there is a "combined" branch (which contains the code of all subbranches, and you can build it and run tests on it - please see the Chained PR workflows
section below).
Now everytime you make a change to any branch in the train, run git pr-train -p
to merge and push branches or git pr-train -rpf
to rebase branches and force-push (if you prefer rebasing).
The .pr-train.yml
file contains simple configuration that describes your trains. For example, the "billing refactor" example from above would be expressed as:
trains:
big billing refactoring:
- fred_billing-refactor_frontend_bits
- fred_billing-refactor_backend_bits
- fred_billing-refactor_tests
#
# ...config for older trains follows...
With this config, fred_billing-refactor_frontend_bits
branch will be the first one in the train and fred_billing-refactor_tests
will be the last.
If you want to merge your branches one by one from the "bottom" as they get LGTM'd (i.e., they compile, pass tests and make sense on their own):
- Merge the LGTM'd branch into
master
- Merge
master
into next train branch (or rebase that branch on top ofmaster
) - Change the GitHub PR base to
master
so that the diff only contains the expected changes - Delete the merged branch from
.pr-train.yml
- Run
git pr-train
to propagate the changes through the train
Note that steps 1-3 are not pr-train specific, that's just how one-by-one workflow generally works.
Sometimes, you may want to split your code into PRs that cannot be merged separately (e.g., code changes first, then tests and snapshot updates last). In those cases it might be useful to have a branch that combines code from all the subbranches - we call that a "combined branch". It points to the same commit as the last sub-branch in the train with the exception that the PR created for this branch would be based off master
(i.e., it will contain the full diff).
The idea is that you get LGTMs for all sub-branches in the train and then the combined branch is what you merge into master
.
If you want to use this workflow, add a combined branch to the train config like so:
trains:
big billing refactoring:
- fred_billing-refactor_frontend_bits
- fred_billing-refactor_backend_bits
- fred_billing-refactor_tests
- fred_billing-refactor_combined:
combined: true
#
# ...config for older trains follows...
Unlike the sub-branches, the combined branch doesn't need to exist when you run the command; pr-train
will make sure it's created, and it points to the last sub-branch in the train. Just make sure it's listed as the last branch in the train config.
Run git pr-train
in your working dir when you're on any branch that belongs to a PR train. You don't have to be on the first branch, any branch will do. Use -r/--rebase
option if you'd like to rebase branches on top of each other rather than merge (note: you will have to push with git pr-train -pf
in that case).
git pr-train -p
will merge/rebase and push your updated changes to remote origin
(configurable via --remote
option).
All your base are belong to us. - CATS
Are you working in a repository that doesn't use master
as the main (default) branch?
For example, newer repos use main
instead.
Or do you have a different branch that you want all PR trains to use as a base?
Add a section to the top of the config like so:
prs:
main-branch-name: main
trains:
# existing train config
You can override the base branch to use when creating PRs by passing the --base <branch-name>
. This takes precedence
over the main branch specified in the config file.
e.g. git pr-train -p -c -b feat/my-feature-base
To have the command output include a link to the PR that was created or updated,
simply add print-urls: true
to the prs
section of the config file.