Pull requests (PRs)
Clean commits
Ideally, every commit in history of the main
branch should:
- Be a focused, self-contained change.
- Have a commit message that summarizes the change and explains why the change is being made, if not self-evident.
- Build (
./setup build --test
). - Pass tests (
./setup test
).
Drafting a PR
PRs should be split into smaller, more focused, changes when feasible.
However, we also want to avoid polluting the history with commits that don't
build or pass tests, or commits within a single PR that fix a mistake earlier in
the PR. While iterating on the PR, the --fixup
and
--squash
flags are useful for committing changes that should ultimately be
merged with one of the earlier commits.
When creating a pull request, we suggest you first create it as a draft. This will still trigger our continuous-integration checks, and give you a chance resolve any issues with those (i.e. broken tests) before requesting review.
Once done iterating, first consider using git rebase -i --autosquash
to clean
up your commit history, and then force pushing to update your PR. Finally, take
the pull request out of draft mode to signal that you're ready for review.
Responding to review feedback
During PR review, please do not rebase or force-push, since this makes it
difficult to see what's changed between rounds of review. Consider using
--fixup
and --squash
for commits responding to review feedback, so that they
can be appropriately squashed before the final merge. git autofixup can also be useful for generating
--fixup
commits.
Merging
When the PR is ready to be merged, the reviewer might ask you to git rebase
and force push to clean up history, or might do it themselves.
For the maintainer doing the merge:
If the PR is relatively small, or if it's not worth the effort of rewriting history into clean commits, use the "squash and merge" strategy.
If the individual commits appear to be useful to keep around in our history,
instead use the "create a merge commit" strategy. There's no need to review
every individual commit when using this strategy, but if the intermediate
commits are obviously low quality consider using the "squash and merge strategy"
instead. Note that since this strategy creates a merge commit, we can still
later identify and filter out the intermediate commits if desired, e.g. with
git log --first-parent main
.
We've disabled the "Rebase and merge" option, since it does a fast-forward merge, which makes the intermediate commits indistingishuable from the validated and reviewed final state of the PR.
A common task is to rebase a PR on main so that it is up to date, perhaps fix
some conflicts or add some changes to the PR, and then push the updated branch
to test it in the CI before merging. Suppose a user contributor
submitted a
branch bugfix
as PR 1234
, and has allowed the maintainers to update the PR.
Then you could fetch the branch to perform work on the PR locally:
git fetch origin pull/1234/head:pr-1234
git checkout pr-1234
git rebase main
<fix conflicts or commit other changes>
git push -f git@github.com:contributor/shadow.git pr-1234:bugfix
git checkout main
git branch -D pr-1234
If it passes the tests, you can merge the PR in the Github interface as usual.