So, you want to contribute to an R package? That’s fantastic!
Here we walk through the process of making a so-called pull request to the praise package. This package is designed to help package developers “build friendly R packages that praise their users if they have done something good, or they just need it to feel better.” You can use praise to construct encouraging feedback by sampling from its collection of positive adjectives, adverbs, verbs, smileys, and exclamations:
library(praise)
template <- "${EXCLAMATION} - your pull request is ${adjective}!"
praise(template)
#> [1] "YEE-HAW - your pull request is groovy!"
We are going to propose a new adjective: “formidable”.
A pull request is how you propose a change to a GitHub repository. Think of it as a request for the maintainer to pull your changes into their repo.
The pr_*()
family of functions is designed to make
working with GitHub pull requests as painless as possible, for both
contributors and package maintainers. They are designed to support the
Git and GitHub workflows recommended in Happy Git and GitHub for the
useR.
A pull request (PR) involves two players, a contributor and a reviewer. To make it more clear who runs which code, the code chunks in this article are color coded: code executed by the contributor appears in chunks with light gray background and code executed by the reviewer appears in chunks with beige background.
# contributor code
# reviewer code
This article assumes that you have already done the Git and GitHub
parts of the setup
vignette and that you have configured a GitHub personal access
token, as described in Managing Git(Hub)
Credentials. A good way to check that you are ready to use the
pr_*
family of functions is to run
git_sitrep()
, which prints info about your current Git,
gert, and GitHub setup.
Specifically, the pr_*()
functions make use of:
create_github_token()
helps you set one up."https"
or
"ssh"
.
usethis.protocol
option to proactively address this."https"
protocol, your GitHub PAT is the
only credential you need. Which is a good reason to choose
"https"
!The first step is to fork the source repository, to get your own copy
on GitHub, and then clone that, to get your own local copy. There are
many ways to accomplish these two steps, but here we demonstrate
usethis::create_from_github()
:
create_from_github("rladies/praise")
#> ℹ Defaulting to 'https' Git protocol
#> ✓ Setting `fork = TRUE`
#> ✓ Creating '/Users/mine/Desktop/praise/'
#> ✓ Forking 'rladies/praise'
#> ✓ Cloning repo from 'https://github.com/mine-cetinkaya-rundel/praise.git' into '/Users/mine/Desktop/praise'
#> ✓ Setting active project to '/Users/mine/Desktop/praise'
#> ℹ Default branch is 'master'
#> ✓ Adding 'upstream' remote: 'https://github.com/rladies/praise.git'
#> ✓ Pulling in changes from default branch of the source repo 'upstream/master'
#> ✓ Setting remote tracking branch for local 'master' branch to 'upstream/master'
#> ✓ Opening '/Users/mine/Desktop/praise/' in new RStudio session
#> ✓ Setting active project to '<no active project>'
What this does:
origin
remote is set to your praise repo.upstream
remote is set to the praise repo owned by
rladies.master
branch is set to track
upstream/master
, so you can pull upstream changes in the
future.Arguments you might like to know about:
fork = TRUE
or fork = FALSE
if you
don’t want to defer to the default behaviour.destdir
to put praise in a specific location on
your computer. You can set the usethis.destdir
option if
you always want usethis to put new projects in a specific
directory.We start the process of contributing to the package with
pr_init()
, which creates a branch in our repository for the
pull request. It is a good idea to make your pull requests from a
feature branch, not from the repo’s default branch, which is
master
here (another common choice is main
).
We’ll call this branch "formidable"
.
pr_init(branch = "formidable")
#> ✓ Setting active project to '/Users/mine/Desktop/praise'
#> ℹ Pulling changes from 'upstream/master'
#> ✓ Creating and switching to local branch 'formidable'
#> ● Use `pr_push()` to create PR.
This creates a local branch called formidable
and we
switch to it (or “check it out”). Now you can work locally, making
changes to files and committing them to Git.
Let’s go ahead and make the change, which is adding the word
“formidable” to the R/adjective.R
file in the package.
Below is the diff and the commit associated with this change.
<img src=“img/pr-functions-diff.png” alt=“Screenshot of the RStudio Git pane showing the file R/adjective.R staged for a commit. The preview of the file highlights the addition of the line”formidabel”, with no comma at the end of the line. The Commit message says “Add ‘formidable’ to adjectives”.” />
You might spot that we made two mistakes here:
Let’s assume we didn’t actually catch these mistakes, and didn’t build and check the package, which would have revealed the missing comma. We all make mistakes.
pr_push()
pushes the local change to your copy of praise
on GitHub and puts you in position to make your pull request.
pr_push()
#> ✓ Checking that local branch 'formidable' has the changes in 'origin/formidable'
#> ✓ Pushing local 'formidable' branch to 'origin/formidable'
#> ✓ Create PR at link given below
#> ✓ Opening URL 'https://github.com/mine-cetinkaya-rundel/praise/compare/formidable'
This launches a browser window at the URL specified in the last message, which looks like the following.
<img src=“img/pr-functions-pull-request.png” alt=“A screenshot showing the diff on GitHub, with the old version of the file on the left, and the new version containing the newly added line ‘formidabel’, with no comma, on the right. There is a green button that says”Create Pull Request”.” />
Click “Create pull request” to make the PR. After clicking you will
be able to choose between draft
PR and actual PR (If opening a draft PR, mark
it as ready for review once you’re done, e.g. after a few more
commits and one new call to pr_push()
).
GitHub will ping the package maintainer and they will review our pull
request. We can view this pull request in the browser with
pr_view()
. And anyone can follow along with this PR rladies/praise#90.
pr_view(90)
#> ✔ Opening URL 'https://github.com/rladies/praise/pull/90'
If we’re lucky, and our pull request is perfect, the maintainer will accept it, a.k.a. merge it. However, in this case, the PR still needs some work. So the package maintainer leaves us comments requesting changes.
<img src=“img/pr-functions-comments.png” alt=“A screenshot of the comments section on the pull request. A comment from a collaborator on the new line says”Did you mean to add ‘formidable’? And can you please add a comma at the end? Thanks!“” />
Being somewhat new to all this, we try to address one of these comments (fix spelling) and neglect the other (forget to add the comma). We make another change and commit it.
<img src=“img/pr-functions-address-comments.png” alt=“A screenshot of the Rstudio Git pane, showing the changed line with”formidabel” changed to “formidable”. The file R/adjective.R is staged for a commit, with the commit message “Fix Spelling!”” />
Run pr_push()
again to update the branch in our fork,
which is automatically reflected in the PR.
pr_push()
#> ✔ Pushing local 'formidable' branch to 'origin:formidable'
#> ✔ Setting upstream tracking branch for 'formidable' to 'origin/formidable'
#> ✔ Create PR at link given below
#> ✔ Opening URL 'https://github.com/mine-cetinkaya-rundel/praise/compare/formidable'
Now the reviewer gets another chance to review our changes. At this point they might choose to just make the necessary changes and push their commits into our pull request to finish things up.
To do so, the reviewer fetches the PR to their local machine with
pr_fetch()
.
pr_fetch(90)
#> ✔ Setting active project to '/Users/gaborcsardi/works/praise'
#> ✔ Checking out PR 'rladies/praise/#90' (@mine-cetinkaya-rundel): 'Add "formidable" to adjectives'
#> ✔ Adding remote 'mine-cetinkaya-rundel' as 'git@github.com:mine-cetinkaya-rundel/praise.git'
#> ✔ Creating local branch 'mine-cetinkaya-rundel-formidable'
#> ✔ Setting upstream tracking branch for 'mine-cetinkaya-rundel-formidable' to 'mine-cetinkaya-rundel/formidable'
#> ✔ Switching to branch 'mine-cetinkaya-rundel-formidable'
#> ✔ Pulling changes from GitHub PR
Fetching the PR creates a local branch for them called
mine-cetinkaya-rundel-formidable
, which is a text string
comprised of the GitHub username of the contributor and the name of the
branch they had created for this PR. pr_fetch()
also then
sets an upstream tracking branch for the local branch that got created
and switches to that branch so the reviewer can make their changes on
the correct branch.
Once the reviewer makes the necessary changes, such as adding the
missing comma, they run pr_push()
to push their changes
into our PR.
pr_push()
#> ✔ Checking that local branch 'mine-cetinkaya-rundel-formidable' has the changes in 'mine-cetinkaya-rundel/formidable'
#> ✔ Pushing local 'mine-cetinkaya-rundel-formidable' branch to 'mine-cetinkaya-rundel:formidable'
#> ✔ View PR at 'https://github.com/rladies/praise/pull/90' or call `pr_view()`
Finally, the reviewer merges our pull request on GitHub. Locally,
they can run pr_finish()
to switch back to the default
branch (usually named main
or master
), pull,
delete the local branch created during the process of interacting with
our PR, and remove the associated remote.
#> ✔ Checking that remote branch 'mine-cetinkaya-rundel/formidable' has the changes in 'local/mine-cetinkaya-rundel-formidable'
#> ✔ Switching back to 'master' branch
#> ✔ Pulling changes from GitHub source repo 'origin/master'
#> ✔ Deleting local 'mine-cetinkaya-rundel-formidable' branch
#> ✔ Removing remote 'mine-cetinkaya-rundel'
Since the reviewer has contributed some code to our pull request, we
can get that code back to our computer with pr_pull()
. This
is optional here, since the full PR has already been incorporated into
the default branch of the source repo (usually named main
or master
). But pr_pull()
can be useful in PRs
if there are a few rounds of alternating contributions from you and the
maintainer.
pr_pull()
#> ✓ Pulling from 'origin/formidable'
#> Performing fast-forward merge, no commit needed
Finally, we can also conclude the PR process on our end with
pr_finish()
.
#> ✓ Checking that remote branch 'origin/formidable' has the changes in 'formidable'
#> ✓ Switching back to default branch ('master')
#> ℹ Pulling changes from 'origin/master'
#> ✓ Deleting local 'formidable' branch
Remember you can see how this whole PR unfolded at rladies/praise#90.
There are a few other functions in the pr_*()
family
that we didn’t encounter in this PR scenario:
pr_merge_main()
is used for getting changes that
have occurred in the main line of development while we have been working
on this PR. If you’re working in a fork, this does
git pull upstream master
. If you’re making a PR from an
internal branch, this does git pull origin master
. This can
be useful to execute in your PR branch, if there are big changes in the
project and your PR has become un-mergeable. This is also useful to
execute whenever you return to the default branch (usually named
main
or master
) and, indeed,
pr_pause()
includes this. This makes sure that your copy of
the package is up-to-date with the source repo.
pr_pause()
makes sure you’re synced with the PR,
switches back to the default branch, and calls
pr_merge_main()
to keep you up-to-date with the source
repo. This is likely something a package maintainer reviewing numerous
PRs will need to use, as they switch back and forth between
reviewing/extending PRs and the main line of development on the default
branch.
pr_resume()
helps you resume work on a PR after
you’ve spent some time with another branch checked out. If you give it
no arguments, it will present an interactive choice of local branches
and indicates which, if any, are associated with a PR.