Skip to content

Git Pull Request Guide

Guy Margalit edited this page Mar 24, 2021 · 2 revisions

Git Pull Request Guide

This pull request guide will go over the tasks needed to maintain your PR ready to merge.

Fork and clone

Before you contribute to the project, first you need to fork it to your github account and then clone YOUR fork. You can do this using the github gh CLI like this:

gh repo fork noobaa/noobaa-core

After your clone is complete, verify that your git remotes look like this (but with your github username instead of mine):

$ git remote -v
origin	https://github.com/guymguym/noobaa-core.git (fetch)
origin	https://github.com/guymguym/noobaa-core.git (push)
upstream	https://github.com/noobaa/noobaa-core.git (fetch)
upstream	https://github.com/noobaa/noobaa-core.git (push)

This means that you have 2 remotes defined - origin is YOUR fork on github, and upstream is the main noobaa repo on github. If you don't see the upstream remote you can add it like this:

git remote add upstream https://github.com/noobaa/noobaa-core.git

That's it - your fork is ready to use.

Keeping your remotes in sync

Whenever you are about to use the upstream branch as a base for new work, you will have to fetch new commits from github:

git fetch --all

Notice that this will not update local branches (e.g master), only remote branches (e.g upstream/master and origin/master), so after running that if new commits were added on the upstream, you will be able to list those commits with git log master..upstream/master --oneline.

Keeping local master in sync

A useful variant of the fetch command allows to fast-forward local branches to match remote branches - the following will fetch upstream/master and then try to fast-forward local master to match it - if you keep your local master always "clean" (i.e you never update it from other sources) this will always succeed:

git fetch upstream master:master

After that, you might notice that origin/master is now out of sync with local master and upstream/master. You can then push from local master to origin/master to keep all 3 masters in full sync:

git push origin master:master

Create a new branch

Before you are about to start working on your changes, make sure you are checking out a new branch. It is important not to mix your work with the master branches to avoid confusion. Before you create a new branch use fetch so that the new branch will be up to date:

git fetch --all
git checkout -b my-branch upstream/master

You can now work on the new branch and make your changes.

Commit and push to origin for backup

As you work on larger changes, it would be wise to commit your work and push to origin to have another backup of the work on github, and some history in case you need to keep some checkpoints along the way. Whenever you do that use:

git status
git add <files...>
git commit --signoff

Notice that --signoff is needed for the PR checks to pass - see https://github.com/apps/dco.

At this point you can add short comments on the commits - these will not be used later and are just for your convenience.

Rebase from upstream

Since the upstream keep on changing as you work locally, once in a while you should fetch and rebase your branch on top of the upstream:

git fetch --all
git rebase --signoff upstream/master

Git rebase will apply all your local commits, one by one, on top of the new upstream work. The more commits you have locally, and the more conflicts you have with upstream changes, the harder it gets to rebase. In some cases if you made many changes back and forth and kept checkpoint commits, it might become hard to rebase all those older local commits which are already obsolete - in these cases it is wise to squash the commits first and only then rebase.

Squash commits

Squashing commits means to combine multiple commits into one. The common way to do this is using interactive git rebase:

git fetch --all
git rebase -i --signoff upstream/master

The interactive rebase will open an editor with the list of commits and an action for each one - by default the action is pick, and in the comment you will see all the possible actions that you can set for every commit. Notice that removing lines from the list of commits will DROP this commit from the resulting branch. To squash you should mark all the commits besides the first one with the s action. After squashing you will always get a chance to edit the combined commit message to a meaningful message - so use it.

Another way to achieve this is with a soft reset which will reset the HEAD to point to the upstream commit, but leave all the changes on top of it as staged for commit:

git reset --soft upstream/master
git commit --signoff

Make sure your final commit message is looking good and descriptive. If you need to amend it use git commit --amend --signoff when your git status is clean.

Push commits to origin after rebase or squash

After rebase or squash we cannot simply push to the origin branch as usual because github will refuse to update the remote branch which seems to have a different history than the local branch. For that we need to use the force:

git push -f

Sometimes this can be dangerous to run push -f because it will override the current remote branch implicitly without making sure to which branch you really meant, so there is a similar syntax that allows to force the push by specifying the target branch:

git push origin +my-branch

Open a PR

When you are ready to open a pull-request and review the change, use the gh CLI or web to create a PR:

gh pr create [--web]

Update the PR

After you get review comments and make further code changes, you will again need to commit and rebase and squash. You can refer to this full flow:

git status
git add <files...>
git commit --signoff

git fetch --all
git rebase --signoff upstream/master

git reset --soft upstream/master
git commit --signoff
Clone this wiki locally