My Git Rebase Workflow
In this document, I’ll explain my Git workflow using the rebase feature. I have been using this workflow for a long time, and it works regardless of the situation be it with single master branch, feature branches and even forking projects and submitting pull requests. While it might sound more confusing at the start as compared to just creating commits and merging normally, hopefully I can shed some insight on the advantages of this method.
What is Rebase?
The first thing to understand about Git is that at the core it is a linked list (or a tree to be technically correct).
While each commit stores diffs and Git uses this to construct the entire repo (which might be a complex process), the order of commits can be moved around with little disruption (assuming they work on different pieces of code).
In this case, we have a feature branch which has two commits. However, in the time that the commits have been made, new commits have appeared on the master branch. If we were to merge the feature branch onto master it will require you to create a merge commit to join the two branches together.
Doing a rebase can be thought of as a “replay”, to apply the commits as though they were being made now. Rebasing the feature branch against master results in:
This effectively changes the feature branch to be based off the latest commit in the master branch, and replays the commits on the feature branch on the master branch.
Why Rebase instead of Merge?
The main advantage of the rebase workflow is that it maintains the commit history and the commit authors without creating superfluous merge commits. This helps when tracking bugs/issues in the code and it is required to get the context of who/why were certain lines of code written the way they are, as opposed to getting distracted by merge commits which might result in the git history showing the wrong commit/author as the most recent committer for a line of code.
It also maintains chronological order of the master branch (while sacrificing the local order). This is something that usually confuses people as they might wonder why a commit that they wrote 2 days ago appears after a commit on the master branch which was written just before their rebase against master. However, when we consider that the master branch is the source of truth, we give priority to commits that were made “public” (pushed to origin
master
) over commits that were made in “private” (locally). This also clears up misunderstanding when two authors may work on the same piece of code, it is the responsibility of the person making the later commit to master to ensure that the their code is compatible (even if they may have written their code before).
Rebase Workflow
I’ll share a workflow that should work for the new proposed branching and PR
- Fetch origin changes
- Merge
origin/master
branch into localmaster
branch - Create a branch
- Write code💻!! Commit Early and Often
- Fetch origin again (in case
origin
master
has had new commits since branch was created) - Rebase against
origin/master
- Push the branch
- Open Pull Request.
1. Fetch Origin Changes
Before doing anything, keep up to date with origin. Fetch the most recent code from the master
branch of origin
. Git will store these contents locally in origin/master
$ git fetch origin master
Origin
Local
2. Merge origin/master
Branch into Local master
Branch
$ git chekout master
$ git merge origin/master
Update the local master
branch to reflect the changes in origin/master
. This will perform a fast-forward merge leaving both master
and origin/master
at the same commit.
Origin
Local
3. Create a Branch
Now, a branch can be created to track new work.
$ git branch feature
$ git checkout feature
or simply:
$ git checkout -b feature
Origin
Local
4. Write code💻!! Commit Early and Often
Do some actual coding here.
Try to make atomic commits: each commit should do a specific functionality. Write good commit messages.
Commits can be cleaned up here. git rebase interactive HEAD~n
opens up the last n commits which allows you to edit the order and how they are applied. It opens up a list of commits that would be applied top to bottom and the commands to apply for each commit. Both the order of the commits and the command itself can be changed. Some useful commands are: edit
, fixup
.
Origin
Local
5. Fetch Origin Again (in case origin
master
Has Had New Commits since Branch Was created)
Before the commits can be merged back, need to grab any new commits that have appeared on origin
.
$ git fetch origin master
Origin
Local
6. Rebase against origin/master
Rebase will change the original commit on which a branch is based. Rebase will result in new commits (with the same commit messages) with new SHA-1 hashes. Typically rebase will the be done against the branch that is intended to be merged into. In this case, it would be origin/master
.
$ git rebase origin/master
Origin
Local
7. Push the Branch
Push the branch to origin.
$ git push --set-upstream origin feature
If there has been a rebase on the branch, it might be required to do a force push
$ git push --force-with-lease origin feature
--force-with-lease
will only force push if the remote branch is in the state you expect (i.e., no one else has pushed to it since your last fetch).
Warning! Be careful when rebasing public branches. Rebase should only be done on branches on which you are certain you are the only one working/locally. If you rebase a public branch (eg. master) and push it to origin, it will result in the history being messed up and cause others who have their older version of the branch to have issues merging their branch.
Origin
Local
8. Open Pull Request
At this point, we can open a Pull Request to get a colleague to review. If there is a need to make any changes to the branch, just follow steps 5-7. Pushing to the branch will update the Pull Request with the changes to the branch.
Once the PR is approved, it can be merged into master
.
Origin
Local
After this, update your local origin/master and master branches using steps 1 and 2.
Some other Tips
Pull (fetch + merge) but using rebase. Autostash helps when you have changes in your directory, it automatically stashes, pulls and pops the stash.
git pull --rebase --autostash