Rebase and Squash

Common practice for teams is to squash or condense long commit message chains into one or a few commits before merging into master .

This is useful when, like me, someone commits frequently and thus would clutter a Git log. Squashing serves to maintain a readable Git log.

Prior to merging a feature branch into the main ( master ) branch, it should be rebased from the up-to-date master .

A pull request, discussed later, will be where all the commits of this branch are squashed down to a single buildable commit and merged into master .

Rebasing essentially ports a branch ( master ) into your current branch by applying all of your commits on top of that branch ( master ), then replacing your branch with the revised version. This catches you up to a branch ( master ) by rewriting Git history in your local Git.

Think of it as moving your branch to the tip of master instead of having branched off from an earlier version of master.

Instead of needing to heavily investigate individual commits from a branch, each merge commit to master should contain all the code for a feature or bug fix. This allows for a much easier investigation process.

While squashing prior to rebasing reduces conflicts due to fewer steps of conflict resolution, it literally changes the history of the repository as documented on GitHub for everyone, and thus is not the most accurate representation.

Rebasing before squashing retains the Git log tidiness and you don’t change history prior to documenting it on GitHub.

Interactive rebase

git rebase -i is super useful in several circumstances, such as needing to quickly remove a commit.

If your team has a policy in which any feature commit must also contain tests for that feature, squashing several commits into one can be helpful.

This would involve git rebase -i HEAD~n and replace n with the number of commits — replace pick on those commits’ lines to squash .

If your project, like the Accord Project, requires a Developer Certificate of Origin sign-off, you may find yourself needing to rapidly change the messages on a series of commits.

Similarly to before, change pick to edit on the commits needing to change and simply git commit --amend -s and git rebase --continue for each commit.

git push -f into the branch for the pull request. Caution: Force pushing can have dire consequences if not used properly, consult others if unsure how to use this.

Generally, if you do not have large, confusing conflicts, -i (interactive rebase) will be overkill.

Rewriting history

While rewriting history with git rebase is extremely useful in some cases, caution should still be taken. Make sure to not interrupt other people’s history and commits on accident.

Caution: Avoid force pushing to a remote branch other people are working on.

Fear

I still fear rebase and squash . Merge conflicts can be more frequent and seem more difficult. I’ve had experiences of losing work due to incorrectly rebasing.

However, if you frequently commit ongoing work, rebasing complications should be infrequent. Fixing conflicts and git rebase --continue can feel intimidating at first, but continue working with it.

A merge commit will be made with these conflicts, but that is important history on how a conflict was resolved. You can always try again if you feel it is going poorly with git rebase --abort and try again — this reverts to before the rebase attempt.

Flow

My recommendation for a general workflow:

Ensure you are currently in master .

→ If working in a fork, fetch:

git checkout master

git fetch --all --prune

git merge --ff-only upstream/master

git push origin master

→ If working in a branch, pull:

git checkout master

git pull origin master Create a new branch for your feature or bug fix

git checkout -b branchName . Make changes with as many commits as necessary. The final commit should build and pass tests. Make sure your branch sits on top of master (as opposed to branch off a branch). This ensures the reviewer will need only minimal effort to integrate your work by fast-forwarding master

git rebase upstream/master . If you have previously pushed your code to a remote branch, you will need to force push. If not, omit the -f tag.

git push origin branchName -f Open a pull request in GitHub from this forked branch. Once this has been merged to master , remember to clear out your branch locally

git branch -D branchName

Merge

As a non-destructive operation, merging can be nice. However, if the master you are working on is quite active, the Git log can be polluted quickly by the extra commit created by a merge operation.

While many developers are uncomfortable with rebasing and resort to merge master on top of their changes, merging is not always the best option.