As a developer, submitting pull requests is a critical function of your role. Pull requests are how all your hard work is reviewed and merged into the existing codebase. It is important to not just execute the steps, but to really understand what is happening under the hood and the purpose each step fulfills.
In this guide, we’ll walk through the entire process of submitting a pull request in GitHub, including cloning the repository, checking your changes into Git (it’s different from GitHub!), and completing the pull request. Not only will we cover command steps and syntax, we’ll discuss the purpose of each command. This will make it easier when you run into issues and need to troubleshoot.
You’ll need to have Git installed and a GitHub account to follow along. You’ll also need to have a terminal and know how to run commands.
It’s also helpful to have a project repository in mind. You can also create a new GitHub repository specifically for this guide.
Each of these sections is also broken down into individual blog posts if you only need to focus on one area.
- How to clone a GitHub repository
- How to run a project from GitHub locally
- How to make and commit changes in a project from GitHub
- How to create a pull request on GitHub
Let's dive in!
How to clone a GitHub repository
GitHub is a remote place to store a repository. However, it is not the best place to make changes to our code. In order to properly run, make, and test changes, we’ll want to take all the files in the repository and put them on our local machine. We do this by cloning the repository.
Definition: Repository
So what is a repository? A repository is like a folder for your project. A repository can be local (saved to your own computer) or remote (saved to a remote or cloud computer). GitHub is, at its core, a remote place to store and share repositories. The GitHub documentation has a thorough description of repositories here for more information.
Fork the repo (optional)
If you are not an owner or collaborator of the repository you’ll be working with, you’ll first need to fork the repository.
The default page for a repository looks like this. There is the username and project name of the repository (1), a main menu that defaults to the Code tab (2), and some action buttons in the upper right (3). Underneath the main menu, we see some information about the branches (4) and some additional action buttons (5).
To fork the repository, click the Fork button. If you have more than one organization you may see a pop-up asking which organization to use to fork the repository. Otherwise the screen will continue and you will eventually see your own forked version of the repository.
Definition: Fork
So what’s happening here? Because we do not own the repository, we have to make a copy of it. A fork is simply a copy of a repository. This allows us to make changes without affecting the original codebase. It’s important to remember that, at this time, the fork is still a remote repository. It is our remote version of the original project repository.
Clone the repo
Distinction: Fork vs Clone
Forks and clones are both copies, but the difference has to do with what changes during the copy. With a fork, the owner changes during the copy (original repo owner > you) but the location of the repo (remote) stays the same. With a clone, the owner stays the same (you > you) but the location changes (remote > local).
This table demonstrates the difference:
Fork | Clone | ||
---|---|---|---|
Owner | Changes (original > you) | Owner | Does Not Change (you) |
Location | Does Not Change (remote) | Location | Changes (remote to local) |
Under the main menu on the repository page in GitHub, there is a green Code button.
Clicking on this button brings a menu with some options for cloning. You can choose to use HTTPS or SSH. HTTPS works across all repositories and also works behind a proxy. It requires you to enter your GitHub credentials when cloning, but does not require additional setup. SSH requires the setup of an SSH keypair, but will make it easier going forward if you plan to communicate frequently with GitHub.
For this guide, we’ll use HTTPS, however I do recommend learning more about SSH, as it will likely be required in professional development settings.
Copy the URL and open a terminal. Navigate to the directory where you want to copy the repository. If you have a workspace or projects folder that works well, otherwise your Desktop or Home directory is fine.
Enter and run:
git clone <copied repository URL>
If all goes well, you’ll see some output ending with Resolving deltas: 100% (x/x), done.
So what have we done with this command?
Here’s where the difference between Git and GitHub becomes important. GitHub, as we know, is a remote place to store repositories. Git, on the other hand, is a local version control system. It is installed on our local computer and helps us keep track of the different versions of our codebase so we can make changes individually without affecting the work of others.
What we’ve done is tell Git (which is why we used a git command with git clone
) that we want to make a copy of the remote repository at the given URL onto our machine. Git will link and manage the connection between the local clone and the remote repository for when we are ready to submit our changes.
Now you can cd
into the newly-created project folder and see all the files and code has been copied.
How to run the GitHub project locally
Install Dependencies
So, we’ve cloned the project repository to our local machine, but we don’t have everything we need yet to run the project locally. It is a best practice that node_modules
folders (which contain all the package dependencies for the project) NOT be included in the project repository. Because of this, we will need to install dependencies using either NPM or yarn.
npm install
yarn install
Now we will be able to run a development version of the project on our computer, which is helpful for seeing and testing our changes.
Update configurations (optional)
If the project needs any specific environment variables to run properly, you’ll need to set these up on your machine.
One example would be if you use a package like dotenv to store sensitive data like API keys locally without checking the data into GitHub where it can be accessed publicly.
You may also need to provide authentication credentials and port information for a local database or API. These are often managed in a configuration file in the root of your project directory.
Additionally, while the majority of dependencies will be managed by a package manager like yarn or npm, there may be additional dependencies needed on your machine to run your code. In many cases, after some initial setup and if you are working primarily in one language or framework, projects can be up and running with a simple npm install
and npm start
, but check the requirements and README for more information.
It’s key to remember that a repository only contains the code and related information. It is up to you to ensure that your machine is actually able to execute.
Start any dependent applications
Once you have configured your project to connect to any databases or APIs, you need to make sure those are up and running. You may also prefer to only start parts of the application to develop or test independently. Keep in mind that if your front end requires access to the API or database to render properly, you may need to start up both to run correctly in a development environment.
Start development server
Finally, you can start your application development server! A development server is one that runs your application while you are developing. For frameworks with hot reloading, you can typically see changes update on your local server once they are saved in your codebase. Running the application locally can also be helpful for testing or to demonstrate the project before it is ready to be deployed in production.
Most projects will have instructions for starting the dev server in the README, but you can also check the scripts section of the package.json. There will likely be a start
, dev
, or serve
script that will run the application locally.
For example, here is a package.json
from a Vue CLI project that uses the command serve
to start a local server.
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"api": "node server.js",
"cypress:open": "cypress open"
}
Note that build
is not a command to run the site locally, but rather build a production-ready version of your application to be deployed.
Once the application is running locally, you can make and see changes as you are developing!
How to make and commit changes in the GitHub project
Create a new branch
If we run git status
in the terminal, we can see the current branch, likely main
or develop
. We don’t want to make our changes directly on the main branch. This is where the magic of Git comes in. We can create a new branch to serve as our separate space to work and make changes.
You can create a new branch with:
git checkout -b <branch-name>
This will also automatically check out or switch to the new branch.
Consider best practices when naming your branch. Branches are meant to be short-lived and single-purpose. You do not, for example, want to have a branch called cecelia-changes
that you reuse for all your work.
There are likely branch name requirements at your organization or for the project. If not, you’ll want to use a convention similar to this format:
<issue-number>-<category>-<description or name>
like:
714-hotfix-broken-login-button
This article from the Microsoft documentation has some great guidelines for branching, and this Gist contains some additional conventions.
Make and save changes
Now on the new branch, you can make and save your changes to the code.
If you are using VS code, you can type code .
to open the current directory in the IDE. Otherwise open the project in the code editor of your choice.
To practice, you can make a small change to your README.md
file. Save the changes in your code editor.
Review changes
After saving your changes, it’s a good idea to run git status
and confirm which files were updated.
The git status
command will confirm our current branch and show which files have been modified. You should review the changed files and ensure there are no unintended changes. Sometimes we may inadvertently alter our package.json
and need to correct it or we may need to add files to .gitignore
.
Once you’ve confirmed the changes are correct, you will record the changes to the repository.
Checking in your changes is a three-step process. We can think of our changes like a letter we want to mail to a friend. At this point, we have a completed letter. We still need to:
- Put our letter in an envelope (add changes)
- Label and seal the envelope (commit changes)
- Drop the envelope in the mailbox (push changes)
These are represented in the next three steps. For a detailed breakdown of recording changes to a repository and the available options through the process, check out this guide from the Git documentation.
Add changes
When we ran the git status
command, we saw the modified files ready to be staged. We can either add these files individually or all together.
git add <file-name>
git add .
This step is like putting the letter inside an envelope. At this point, we could add more files to be staged, just like we could add more pages to our letter because we have not yet sealed the envelope.
Commit changes
We then commit our changes using the command:
git commit -m "commit message here"
By using the -m
flag we can pass a message via the command line. If you prefer to use a text editor to write a longer commit message, you can just run git commit
and it will prompt a text editor to open.
Just like with our branch name, it’s important to ensure our commit messages are helpful. Your organization will likely have a standard for messages, particularly if they use a tool like semantic-release, which automatically manages which commits are associated with releases. Commit messages may also be automatically associated with issues or other tools as well. For example, here is the commit message format used by Angular.
fix($compile): couple of unit tests for IE9
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Closes #392
Breaks foo.bar api, foo.baz should be used instead
After executing our command, we can confirm with git status
that there are no changes in the working directory. This means we have sealed the envelope. We can either drop it in the mailbox (AKA, push our changes) or wait until we have more envelopes and deliver them all at once.
How to push changes and create a pull request
How to push changes to GitHub
Git connects our local repository to the remote repository in GitHub by keeping track of the remote URL. This is the link that allows us to push our changes. We can see what remote URL Git has for our project by running:
git remote show origin
This will show the URL of the connected repository, as well as what branches are being tracked remotely and locally.
You’ll notice that your current branch, the new one we created with our git checkout -b <branch-name>
command, is not on the list. This is because the local branch is not yet connected to the remote repository.
We can initiate the push with the command:
git push origin <branch-name>
By pushing our changes, Git will essentially send a message to the GitHub repository (managed by the remote URL) with the changes and the new branch information. GitHub will automatically create the new branch and track the corresponding changes. The origin
refers to the remote URL (which is why we ran git remote show origin
earlier) and the branch name is the destination for the changes.
It’s important to note that pushing changes will push to whatever branch name you pass with the command. Make sure you pass the correct branch name so you do not accidentally push your changes directly to a main branch. In most cases, there will be protections against this, but as a best practice, confirm your branch name whenever pushing changes.
If you try to make an unauthorized push, you will receive an error message. You may also get an error message if your local branch is no longer aligned with the remote branch. This can happen if multiple people are working on the same branch at the same time.
If the push is successful, you will see a message in your console stating you can open a pull request using the given link. You can use this URL or visit the repository in GitHub directly in your browser to initiate the request.
How to initiate a pull request
Now that our changes are on GitHub, we can create our pull request. There are a few different ways to do this -- you can use the GitHub website, the GitHub Desktop application, or the GitHub CLI.
We’ll walk through creating a pull request on the GitHub website. If you visit the repository in GitHub shortly after pushing your changes, you’ll see a yellow banner prompting you to compare changes and start a pull request. You can click this banner to start the pull request process. The vast majority of the time, I’ll use this banner.
If there is no banner because I haven’t recently pushed changes, I prefer to initiate a pull request from the branches page. This helps me ensure I’m starting the request from the correct branch.
From the repository, click the branches list to navigate to the branches page.
Here, you can initiate a pull request for any branch that has not yet been merged.
Find the branch with your pushed changes and click the ‘New pull request’ button. This will take you to the ‘Open a pull request’ screen.
Breakdown of the pull request page
Section 1 shows what branches are under comparison. It also will check if the branches can automatically be merged. This does not mean that your pull request can be automatically approved. It means that GitHub has compared the branches and did not find any merge conflicts. It’s best practice to resolve any merge conflicts before submitting a pull request.
Section 2 is where you fill out the content of your pull request, including the title and comment. A well-written pull request is critical to ensuring the reviewer can understand your code and its purpose. Typically organizations will use a Pull Request Template outlining what is required before submitting.
If there is not a pull request template, review the project’s Contributing Guide for any formatting requirements. You can also check out these resources on writing pull requests:
Section 3 is a sidebar with additional information about the pull request. This sidebar appears even after the pull request is created. Here is where you can assign a reviewer, add a label, and adjust other fields associated with the project, such as milestones or linked issues. You may not see all of the options in the screenshot, depending on what options or add-ons are enabled in the GitHub project.
Finally, Section 4 shows the content of the pull request. Here is where you’ll see all the commits you’ve made, what files were changed and the comparison of the changes (called a diff), as well as any comments or other contributors. You’ll want to review this section and validate that the commits and changes are correct.
How to submit a pull request
Once you have written the pull request, you can submit using the green “Create pull request” button. If you click the drop-down arrow instead, you have the option to Create draft pull request. This feature allows you to create a pull request that is not yet ready for review. You can then continue to make commits until the pull request is ready. Read more about draft pull requests here.
Once you’ve created the pull request, it’s ready for review! You can read more about status checks and pull request reviews in the GitHub documentation.
Not every pull request process will go smoothly. You may (and will) run into authentication issues, merge conflicts, or need to revert changes. GitHub documentation is excellent for troubleshooting, and remember that Google is your best friend when facing issues.
If you use GitHub frequently, check out my post on automatically creating GitHub issues with Node.js and the GitHub API.
Top comments (3)
Hi Cecelia,
thank you for your post, this was very helpful for me as a first-timer.
Once I have gone through the full cycle, and the pull request is out of the door, what is the recommended best practice for cleaning up local stuff, keeping a forked repo on GitHub, etc.?
Thanks,
Bert
Good question! I don’t usually clean things up after, but posted on Twitter to get some feedback on best practices. I’ll follow up with additional info!
Deleting feature branches upstream (in the remote repository you forked) and any artifacts created in CI/CD was recommended. Generally trying to keep the shared areas clean like GitHub but not worrying about local branches.