In this chapter we’ll start by looking at how to collaborate on a repository that you don’t have permission to push to by creating a fork and a pull request. While forks are a good way to accept contributions from people you don’t work with regularly, they are a bit too cumbersome for everyday use in a team that is working together closely. Because of this, later in the chapter we’ll look at how to collaborate directly on a single repository without using forks. Lastly, we’ll take some time to look more deeply into collaborating using pull requests and issues.
If you want to contribute directly to a repository, you either need to own it or have been added to it as a collaborator. If you want to contribute to a repository that you don’t own and are not a collaborator on, you’ll need to make a copy of it on GitHub under your user account. That process is called forking. Once you’ve forked a repository, you’ll be able to make any changes you want to your fork (copy) and you’ll be able to request that your changes get incorporated into the original repository by using a pull request. Let’s go through that process now.
Go to https://github.com/pragmaticlearning/github-example. Click the Fork button in the top-right corner of the page, as shown in Figure 4-1.
When you click the Fork button, if you are a member of any organizations, you’ll see a list of all of the organizations you’re involved with as well as your username. You’ll be asked where you want to fork the repository (as shown in Figure 4-2).
After you select where you want to fork the repository, or if you are not a member of any organizations, you’ll be taken to your new repository page. Once you’ve forked the repo, you can make any changes you want to your fork (copy). In the next section we’ll look at how you can add a new file, like we did in Chapter 3, and then how to create a pull request to try to get your change incorporated into the original repository.
In this section we’ll look at how to add a new file to a repository. As a reminder, you can see in Figure 4-3 that there are a collection of buttons on the right side of the screen above the listing of files, one of which says “Create new file.”
Click this button and you’ll be taken to the same new file screen as before, shown in Figure 4-4.
Toward the top of the page is a text box just to the right of the project name, where you can enter the name of the file you want to add to the project. Below that is a text area where you can enter the content you’d like to put in the file. Scroll down the page when you’re done naming the file and entering the content; as shown in Figure 4-5, you’ll see a couple of text boxes where you can create a (required) short description and an optional extended description of the change that you’re making.
These descriptions will be saved as the commit message for your commit. If you don’t enter anything, the default commit message will be “Create (<filename>).” As mentioned in Chapter 3, you’ll want to enter a meaningful commit message so other people viewing the project will understand what you did and why you did it. As shown in Figure 4-5, you have two options for where to commit this change: directly to the
master branch or on a new branch to propose the change as a pull request. We’ll start a pull request another way in a moment, but this “quick pull request” flow exists for immediately starting a pull request from your edits. For now, click the green “Commit new file” button: your new file will be added to the project and your commit will be added to the commit history. You can see in Figure 4-6 that first-file.md has been added to the list of files.
You’ve made a change to your fork of the project, but the change hasn’t propagated back to the original project yet. That makes sense. Anyone can fork any public project, and the project owner wouldn’t want just anyone editing all of their files. However, sometimes it’s great to allow other people to propose changes to a project. This allows a large number of people to easily contribute to an open source project or a smaller team to work together on an internal project. That is what pull requests are for. Some maintainers send pull requests to their own repositories for the sake of documenting how they’ve developed their projects, even if they’re not waiting for anyone else’s approval! Many more approaches to maintaining, contributing, and working with open source software in general can be found at https://opensource.guide.
With a pull request, you can request that changes you’ve made on a fork be incorporated into the original project. Let’s go through the process now. As you can see in Figure 4-7, at the top of the page there’s a “Pull requests” tab.
Click the “Pull requests” tab, and you’ll see a screen similar to Figure 4-8 showing that currently you don’t have any outstanding pull requests. Click the green “New pull request” button at the top right of the screen.
When you click the button, you’ll see a screen similar to Figure 4-9.
One of the first things you’ll see in Figure 4-9 is that it is proposing a pull request between
brntbeer:master. Pull requests are requests to incorporate the changes from one branch (stream of history) into another. In this case, GitHub has correctly guessed that I want to take the change that I made on the
master branch on my fork (the new file I added) and have that merged back into the
master branch on the original project that I forked from. Note that the branch with the changes that you want merged in is on the right, and the target branch you’d like it to be merged into is on the left.
As you look lower down in Figure 4-9, you’ll also see that it provides a summary of the changes that would occur if that pull request was merged—I did indeed make one commit that changed a single file. It even shows in green the new content that would be added to first-file.md—this is often called the diff or difference. If I click the Split or Unified button, it will change the way in which the difference that is being proposed is rendered. This setting is “sticky,” meaning that GitHub will remember it for future diff renderings.
Once you’ve confirmed that the proposed pull request is the one you want to create, the next step is to click the large green “Create pull request” button. Doing so will take you to a page similar to Figure 4-10.
This screen is your chance to tell the story and start a conversation about why your changes should be incorporated into the other project, so take the time to create a meaningful title and description of the changes you’ve made. By default the title will be the first line of your commit message for your most recent commit, and if you’ve made more than one commit on the branch you’re trying to have merged, the description will contain a bulleted list of the first lines of all of the commit messages that are part of the pull request. That’s a fine starting point, but you’re going to want to take a little bit of time to describe not only what changes you’ve made, but why you made them and why they’d be a good addition to the project. Since this is the start of a conversation, it’s often best to @mention (pronounced “at-mention”) the maintainer to ensure they see your request, and to let them know if you still have some changes you’d like to make or not. If you’re lucky, the repository you’re contributing to will have made use of an issue or pull request template to fill in some information or give you some instructions on contributing. For more information about these templates, please see the GitHub documentation.
You may also notice the “Allow edits from maintainers” checkbox below the description section, to the left of the “Create pull request” button. This allows the maintainers to make changes directly on your branch. This may sound scary at first, but it really helps the maintainers, and you, if there are small changes to be made before accepting your changes. Sometimes there may be simple stylistic changes or complicated changes that are easier for them to do to finish the pull request. If you don’t select this option here, it also can be turned on or off from the pull request screen after the pull request is created.
Once you’ve finished describing your pull request, click the “Create pull request” button and you’ll see a page that looks like Figure 4-11.
There are a couple of things that you should notice in Figure 4-11. First, notice that you’re now in the original project, under
pragmaticlearning. This makes sense. You wanted to create a request to pull your work into that project, so the pull request is part of that project—not your fork. You can see that “brntbeer wants to merge 1 commit into
brntbeer:master,” and it shows you the pull request (title and description) followed by the commit that was made. Clicking that commit displays the details of the commit in a review workflow, as you can see in Figure 4-12.
Notice that the commit link has taken you to the “Files changed” tab of the pull request. This tab is where you can see all of your proposed edits for this pull request. However, in this situation you see just that one commit because that’s what you clicked on. This is a useful review workflow when you want to jump through some work one commit at a time. We’ll cover code reviews for pull requests in more detail later in this chapter.
Going back to the pull request in Figure 4-11, you’ll see that there is an option to merge the pull request. That option is visible only to the owner of the project or to anyone the owner has added as a collaborator with “write” or “admin” permission. If someone without those permissions—for example, yourself—was looking at the page, he would not be able to merge the pull request. To illustrate, in Figure 4-13 I’ve logged in as another user. When I view the same page, I don’t get the option to merge in the pull request, although I can still comment on it if I want.
Often there will be a discussion before a pull request is merged, but we’ll look at that more later in this chapter. For now I’m just going to accept the pull request and merge it in. Clicking the “Merge pull request” button adds a text box where I get the option to customize the commit message for merging the pull request, as shown in Figure 4-14.
Once I’ve made any changes I want to the merge commit’s message, I can just click the “Confirm merge” button below and to the left. The pull request is then merged, and the output is displayed, as in Figure 4-15.
Notice that you can still see the pull request message and the commit, but now you can also see who merged in the pull request and approximately when they did so. I also have the ability to revert this pull request if the merge was done in error. The Revert button here will allow me to open a new pull request that does the inverse of the work I just did. Finally, if you look at the project page in Figure 4-16, you’ll notice a couple of things.
First, first-file.md has been added to the project. Second, there are 10 commits now in the original project. Clicking the “10 commits” link shows why (see Figure 4-17).
In addition to the eight original commits in the project, there is the “Create first-file.md” commit that was made on my fork and a new merge commit that brought the work into the original project when I merged the pull request. By default, whenever you merge a pull request, it will create one of these merge commits (it is possible to configure the repository to have different functionality, but we’ll leave that as an advanced topic for you to learn about later). They are really useful because the commit message, which you can edit when you merge a pull request, allows you to document why you decided to include the work.
If you ever wanted to get rid of all of the work you merged in from a pull request, you could ask one of your developers to “revert the merge commit for that pull request” and she’d be able to easily remove all of the changes that got merged in. If you have permission to merge the pull request, you will also have the ability to bring in the changes by squashing them all together before merging, or rebasing before merging. These are more advanced workflows that we won’t be covering in this book. If you are interested in learning more, you should check out the GitHub learning resources.
Now that we’ve seen how to contribute via a fork, we’ll look at a more common team-based workflow: committing directly to a repository that you have access to push code directly to. To some people, this workflow may seem like a combination of working on a fork as well as working on your own repository, with one exception: feature branching. In Git, everything is committed onto a branch, and the branch we’ve done all of our commits on so far has been
master. An alternative to committing to
master is to create a branch that is often named after what you’re working on. Sometimes this is something simple, like
update-readme-with-contact-info, and other times it’s named directly after a work item that’s been assigned to you by a project management team, like
Besides allowing you to work safely and experiment on changes without affecting the
master branch (which often signifies the safe, nonbuggy, stable code base), feature branching allows you to start a pull request in the same repository you’re in. This is the part that is similar to the forking workflow. An example of the type of workflow you will be using is shown in Figure 4-18; it’s called the GitHub Flow. Keep this image in mind as you dive in and experience it for yourself.
If I want to augment the README.md file, the first thing I should to do is create a branch. That way I’ll be able to keep my changes separate while I’m working on them and can open my pull request later. To do that, I can just click the “Branch: master” drop-down, which lists the current branches in the project and provides a text box for entering the name of an existing branch or the new branch that I want to create (see Figure 4-20).
If I create an
update_readme branch, as you can see in Figure 4-21, GitHub automatically checks out that new branch. You can see this both on the Branch pull-down where the current branch is displayed, and in the browser URL bar: the address ends with tree/update_readme, signifying that we’re on the
The next step is to start to make changes on the
update_readme branch. I’ll edit the README.md file and commit the changes like we’ve done a few times up to this point. As you can see in Figure 4-22, I have only one commit on the
master branch, but if you look at Figure 4-23, where I’ve changed the branch to
update_readme, in addition to the initial commit you can also see the new commit that I made on the
I might continue to work on the branch for a while, getting my changes just right. Once I’m ready to get some input, I’ll want to create a pull request to start a conversation about my proposed changes.
There are many ways to create a pull request, but as in the previous chapter, I’ll click the Pull Request tab on the top part of the page and then click the green “New pull request” button. When I do this, as you can see in Figure 4-24, the experience is slightly different. Now GitHub isn’t sure what branches I want to create a pull request between, so I have to tell it.
On the left you can see “base: master.” That is perfect as it means that if I create a pull request, once it is accepted, it will get merged into the
master branch, which is exactly what I want. However, I do need to click the “compare: master” drop-down to tell GitHub what branch I want to create a pull request for, as you can see in Figure 4-25. The “compare:” branch is the one that I’d like people to consider merging into
Once I’ve selected a branch, the process is just the same as it was earlier in this chapter when creating a pull request from a fork. I click the green “Create pull request” button, enter a title and description to explain the reason for the pull request, and then click the “Create pull request” button. This creates the pull request shown in Figure 4-26.
Pull requests are designed to start a conversation about a proposed change—usually either a new feature or a bug fix. Originally, pull requests were created only when coding was completed to ask someone to incorporate a completed set of changes, but these days pull requests are used in a couple of different ways.
If you have a change that you’re confident about, you can still create a new branch, make all your changes, and wait to create a pull request until you’re done with the work. In such a case, the purpose of the pull request is just as a double-check to make sure that the rest of your team agrees with the changes you made before the changes get merged into
master and pushed to production.
However, there is another way to use pull requests. In many companies, employees will often create pull requests for features that they’d like to discuss. So, if you have an idea for a change but aren’t sure whether it’s a good idea, consider creating a branch and making the simplest possible start on the work—maybe just a small text file describing it. Once you have a commit on the branch, you can then create a pull request to kick off a discussion about the idea.
If you’ve created a pull request and would like feedback from specific people on a team, @mention them. To do this, within the pull request itself or in a comment on the pull request, type @ and then type in the GitHub username. If the person is the owner or a collaborator on the repository, the username will autocomplete as you start to type. You can also begin typing the user’s displayed name (which can be set in your public profile).
If you wanted to get feedback from me on some work you’d been doing, you might create a comment like “hey @brntbeer, mind looking at this PR and letting me know what you think?” The formality of the language will depend on the people you’re working with, but pull request comments are often written in a fairly informal style.
On most projects there should be only a few pull requests open at any one time. A good rule of thumb for a private repository is that you shouldn’t have more than a few open pull requests per developer. Generally, the fewer pull requests you have open, the better, as it is more valuable to keep the team focused on finishing up existing features than on starting new ones. Also, pull requests should be for small, iterative changes, to make them easier to review. The more changes that go into a branch, the longer it will live and the more difficult the changes will be to review properly. These “long-lived” branches aren’t always avoidable, but you should be on the lookout for them.
The number of open pull requests on open source projects will typically be much larger, as anyone can create a pull request, and sometimes it takes a while for the core project team to review, accept, and/or close them.
When you find a pull request that you want to review, click it to view the pull request detail page.
A really important part of working with a development team is to take the time to review all of the pull requests that you might care about. Nothing is more disheartening than to work on a feature for a couple of days, create a pull request, and then get no feedback at all. Also remember that by default anyone can merge their own pull request into
master so long as they have write permission, so make sure to take the time to review people’s work so they aren’t tempted to merge it in without at least one or two people having a look at it. Or, if you’d like this not to be the case, you can use protected branches to require certain approval workflows. We’ll cover some of these options in Chapter 7.
Whenever you get an email or a web notification that you’ve been @mentioned in a pull request, make sure to take the time to check it out as soon as you can and provide some useful feedback. Sometimes useful feedback may even be to let the person know you’ll give it a good review soon. Even if you’re not named personally, taking a little bit of time out of your day to make sure that you review any outstanding pull requests and provide your thoughts to ensure everyone is on the same page with where the project is going is a good idea.
Commenting on pull requests is pretty simple. Skim down the pull request page, go to the comment box, type in your feedback, and click the Comment button.
Especially for a team that doesn’t work in the same office all of the time, commenting on pull requests is often one of the more frequent ways that team gets to interact. Because of that, it’s often a good idea to add a little bit of fun to the interactions.
GitHub has built-in support for emoji. Emoji are small images that are often used for displaying a mood or emotion graphically. If you look at Figure 4-27, you’ll see that this comment has the :+1: (I’m in support of this feature) emoji and the :ship: (let’s merge this in and “ship” it) emoji.
Another way to add some more color to your comments on GitHub is by using animated GIFs. While emoji are subtle, most animated GIFs are much larger and more striking—they’re often a great way to really lighten the mood or show strong support (or disapproval) for a change or a comment. To add an animated GIF (or any other image) to a pull request, just drag and drop it into the comment box and it’ll get uploaded automatically.
Sometimes you’ll want to make a change directly to a pull request. Perhaps someone has added a new page and you’d like to fix up the marketing copy, the legal disclaimer, or even the CSS to make it display better in your favorite browser. It’s easy to make a change to someone else’s pull request.
The process is the same as for editing a file, which we covered in the previous chapter. The only difference is that you must be on the correct branch. In this case I’m looking at the
update_readme pull request for adding some content to the README.md file, as you can see in Figure 4-28.
If I decided that it would be great if the file contained a brief description, rather than just commenting that the README was missing a contributors guide, I could add one.
To make the change, all I need to do is go to the repository home page and select the
update_readme branch from the drop-down list of branches. I can then click the file and click the edit icon, and I’ll get the edit screen, as you can see in Figure 4-29.
I can then make my changes, scroll down the page, and enter some kind of commit message, as shown in Figure 4-30.
Now if I go back to the pull request page, you can see in Figure 4-31 that my commit has been added to the pull request. Anyone who is watching the pull request will get a notification that it has been updated so they can review my change and provide their feedback.
If you have the appropriate permissions, before you approve a pull request that includes substantive code changes that you can’t just review visually, you’re going to want to download a copy of the repository (clone the repo). Then check out the branch that the pull request relates to, run the automated tests to make sure they’re all passing, and then run the code and maybe do a little bit of manual testing just to make sure it seems solid. We’ll cover cloning repositories in Chapter 8.
If you’re not a developer, you could leave this to your development team, but you do want to make sure that at least one or two people are downloading the code, running the test suite, and maybe doing a little manual testing before approving a pull request. Alternatively, an easier and best practice option is to set up automated testing that will run for you and report its status back to the pull request. This, as well as required reviews, can be configured inside of the protected branches settings of a repository, which will be talked about in Chapter 7.
When you’re ready to merge a pull request, just click the large green “Merge pull request” button, as shown in Figure 4-32.
When you do so, GitHub will ask for a commit message (the default will be the title of the pull request and an indication that this commit came in from a pull request merge), as shown in Figure 4-33. Once you’ve entered that, just click the “Confirm merge” button and the pull request will get merged and closed, as described earlier in this chapter.
You should have some kind of policy for closing pull requests. Many teams will require one or two people other than the primary author of the pull request to provide a :+1: before a pull request is merged. Have some kind of process, but keep it as lightweight as needed. Remember, you can always revert a merge, so if the code you’re working on does not have the possibility of endangering anyone’s life, it’s generally better to “move fast and (occasionally) break things” than have a list of 27 people who need to approve every single pull request before it can be merged. However, as mentioned a few times already, if there are strict requirements they can be added in the protected branches and required statuses.
One question that often comes up is whether a pull request should be merged by the person who created the pull request or by someone else. I generally recommend that pull requests be merged by the person who created them. Here’s why.
Many companies have the rule that “the person who created a pull request can’t merge it.” The reason for this is to make sure that someone doesn’t just create a pull request and merge it in without getting any feedback. The idea is good, but I don’t think the recommendation is ideal.
Most of the time, the person who created the pull request is the person who knows the most about it. As such, I always want to have that person available when her work is merged in just in case it breaks something unexpected. One of the easiest ways of making sure that she’s around is to ask her to do the merge. So I’d recommend asking people to merge in their own pull requests, but making it clear that they shouldn’t do so until they’ve got at least a couple of :+1:s from the rest of the team, or any other required workflows and statuses have happened.
If you create a pull request, comment on one, commit to one, or are @mentioned in one, by default you’ll be subscribed to the pull request. This means that whenever anyone comments on, commits to, merges, or closes the pull request, you’ll be sent a notification. You can see on the right side of Figure 4-34 that I am currently subscribed to this pull request.
If you’re no longer interested in a pull request that you’ve been subscribed to, just click the Unsubscribe button and you’ll stop receiving notifications. You will get re-subscribed automatically if anyone @mentions you again in the comments. If you’re not subscribed to a pull request that you’d like to keep an eye on, just click the Subscribe button, as shown on the right in Figure 4-35, and you will start getting notifications of any activity on that pull request.
Up to this point, you’ve had an overview of the repository, working by yourself, and working with others. Most of the actual work on GitHub is conducted around pull requests, but what about when you just want to discuss an idea before it becomes work, or if you notice a bug in someone’s software and you want to talk about it? That’s where GitHub issues come in. If pull requests are where you discuss and collaborate around code, GitHub issues are for discussing ideas and planning before a pull request is created. Continue on to the next chapter to find out more!