Step-by-step guide for creating a feature or bugfix branch, submit it for code review as a pull request and once approved, merge upstream. This guide is intended for internal developers with push access to all relevant repos.
You should understand rebasing and squashing. For a very good explanation on rebasing and squashing with pull requests see How to Rebase a Pull Request. Also worth reading is the Hacker's Guide to Git.
$ git clone [email protected]:{username}/project.git
There are different philosophies for naming remotes. The Github standard is to use origin
for your forked repo and upstream
for the original repo. Another naming strategy is to use descriptive remote names and use <your name>
instead of origin
and <project name>
instead of upstream
. This is particularly interesting when you also need to pull somenone else's branch; you then create a remote with that person's name and your naming remains consistent.
For the sake of the examples below, we will use origin
and upstream
.
git remote add upstream [email protected]:user/project.git
- at this point you can push commits to your forked repo with
$ git push origin master
- and you can pull in upstream changes
$ git fetch upstream
$ git merge upstream/master
Add this at the end of .git/config
[alias]
pr = "!f() { git fetch upstream pull/$1/head:pr/$1; }; f"
This can be used to fetch a pull request locally using
$ git pr 1234
For handling frequent rebase/merges (especially with long-lived branches) it's handy to enable the rerere
cache by adding:
[rerere]
enabled = 1
to ~/.gitconfig
(or run git config --global rerere.enabled true
). This cache can be used to resolve merge conflicts that you have resolved once before, using the cached version of the resolution.
$ git checkout -b feature/<feature name>
$ git checkout -b fix/<issue id>
For example, lets pretend 2 commits:
$ git commit -m “some change” file_a
$ git commit -m “some other change” file_b
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
$ git checkout feature/foobar
$ git rebase master
If you rebased, you are changing the history on your branch. As a result, if you try to do a normal git push after a rebase, git will reject it. Instead, you'll need to use the -f
or --force
flag to tell git that you really know what you're doing. Note that you typically want to avoid using -f
on shared branches.
$ git push origin feature/foobar -f
On Github in your forked repo, submit the pull request for the branch
In the review process, you can create new commits and push them to your forked repo to update the pull request.
$ git commit -m “review fix” file_a
$ git push origin feature/foobar
Once the code review is complete, merge the branch upstream
It is not absolutely required to squash commits. For bigger changes or refactoring the changeset makes more sense with separate commits. Also you should never squash commits for the pull request submission. Squashing must happen only after the review process, just before merging.
- You can squash using your branch base commit with
merge-base
to retrieve the base commit hash:
$ git merge-base my-branch master
$ git rebase -i <base commit hash>
- Or you can squash on the last N commits:
$ git rebase --i HEAD~N
This will result in presenting an editor with something like:
pick f73ed64 some change
pick b8d914c some other change
# Rebase b710536..b8d914c onto b710536
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
For every line except the first, you want to replace the word pick
with the word squash
. It should end up looking like this:
pick f73ed64 some change
squash b8d914c some other change
Save-quit the editor, and you’ll get another editor with something like:
# This is a combination of 2 commits.
# The first commit's message is:
some change
# This is the 2nd commit message:
some other change
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto b710536
# You are currently editing a commit while rebasing branch 'testflow' on 'b710536'.
#
# Changes to be committed:
# new file: some change
# new file: some other change
#
# Untracked files:
# .ruby-version
#
You can add more comments at the end of the file. You should add on the last list the reference to the issue it closes which will automatically close the issue
closes 1234
Save-quit the editor
First update master
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
Rebase branch against master
$ git checkout feature/foobar
$ git rebase master
If you did a rebase/squash, you are changing the history on your branch. As a result, if you try to do a normal git push after a rebase, git will reject it. Instead, you'll need to use the -f
or --force
flag to tell git that you really know what you're doing. Note that you typically want to avoid using -f
on shared branches.
$ git push origin feature/foobar -f
If the commits were not squashed, optionally use the --no-ff
to merge so that git always add a merge commit to maintains the history of the feature branch.
$ git checkout master
$ git merge feature/foobar
$ git push upstream master
This will automatically close the pull request and close the referenced issue in the commit comment.
Do not forget to back-port your commit in the release branches, for example in 1.x.
Checkout branch
$ git checkout 1.x
$ git fetch upstream 1.x
$ git merge upstream/1.x
Merge commit
$ git cherry-pick {commit hash}
Push upstream
$ git push upstream 1.x
Label the PR/issue with the branches it was back-ported into, for example with the v1.x label.
Make sure you added the pr alias as described in the setup section.
$ git pr 1234
First update master
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
Rebase branch against master
$ git checkout pr/1234
$ git rebase master
$ git commit -m “some change” file_a
$ git commit -m “some other change” file_b
Once the code review is complete, merge the pr upstream
See earlier section on commits squashing
If the commits were not squashed, optionally use the --no-ff
to merge so that git always add a merge commit to maintains the history of the feature branch.
$ git checkout master
$ git merge feature/foobar
$ git push upstream master
This will automatically close the referenced pull request and close the referenced issue in the commit comment, by addding
closes #1234
Do not forget to back-port your commit in the release branches, for example in 1.x.
Checkout branch
$ git checkout 1.x
$ git fetch upstream 1.x
$ git merge upstream/1.x
Merge commit
$ git cherry-pick {commit hash}
Push upstream
$ git push upstream 1.x
Label the PR/issue with the branches it was back-ported into, for example with the v1.x label.