LinkORB Engineering

Squash related commits into one in [#git]

Team members’ contributions to a pull request (PR) can quickly increase the PR’s commit history when collaborating on a project. You accept a small typo fix here, a great suggestion there, and before you know it, you’ve added 100 commits to a PR you planned to keep small. While, LinkORB prefers to keep the size of PRs small, having a small PR with a large number of commits can make it difficult to review, undo, or track changes.

To maintain a cleaner, more manageable commit history, we recommend squashing similar and related commits on a child branch before merging a PR. Squashing is the process of merging similar and related commits into one before or after pushing changes upstream.

This document provides guidance on how to squash commits using:

Specifically, it discusses:

How squashing works

Git provides a rebase command that allows users to rewrite the history of a repository by:

  1. Selecting some (or all) commits on a branch.
  2. Squeezing all changes from the selected commits into a staging area (uncommitted branch).
  3. Creating a new commit that treats the merged commits as one (new) commit.
  4. Erasing previously logged individual entries of the merged commits from the commit history and replacing them with a single commit containing all the squashed changes.

Assuming you’ve been assigned the task of customizing a web app template and updating the app’s documentation. The commit history of your branch may look similar to the below example.

82af5ac (HEAD -> customize-template-4096) docs: paraphrase sentence #4096
5c2195e docs: fix typo #4096
73a05c3 docs: update README.md #4096
a8f3604 fix: missing semi-colon (pesky eslint!) #4096
852495f refactor: reduce login controller length #4096
c2c9c96 feat: initial commit (definitely buggy) #4096

Although each of the three latest commits (prefixed by docs) introduces a different change, taken together, they simply update the app’s documentation (in the README.md file). For this reason, you may clean up the commit history by squashing all three commits into one. The commit history example above will look similar to the following after all docs changes are squashed:

30a2041 (HEAD -> customize-template-4096) docs: update README.md #4096
a8f3604 fix: missing semi-colon (pesky eslint!) #4096
852495f refactor: reduce login controller length #4096
c2c9c96 feat: initial commit (definitely buggy) #4096

When should I squash a commit?

Consider squashing related commits when a PR:

  • Ships a non-complex feature or fix that has a large commit history.

  • Contains multiple commits that are similar or related. For example, three subsequent related commits may be squashed into one if the last two fix an error in the first.

  • Includes multiple commits that can be grouped into logical units. For example, you may squash 30 commits that contain code and documentation changes into two (one docs and one feat) commits.

    Organize and squash related commits into logical units instead of squashing all commits into one.

  • Only contains documentation.

    Documentation commits are great candidates for squashing. However, commits should be squashed with discretion when they contain functional code. Look out for and resolve all rebase conflicts in messages and alerts shown after each step before finalizing the squashing operation.


    Squashing is recommended but not required. If squashing related commits takes more than 90 minutes or results in difficult-to-resolve conflicts, please abort (e.g., git rebase --abort) the squashing operation and request that the PR be merged as-is.

How to squash commits using GitHub Desktop

Squash related commits using GitHub Desktop as follows:

  1. Download and install GitHub Desktop if it is not already installed on your computer.
  2. Depending on where the repository is stored, select Add local repository or Clone repository from the File menu, then find and open the repository whose commits you want to squash.
  3. Click Current branch at the top of the screen and select the branch you want to work on.
  4. Click History in the left sidebar.
  5. Hold down the Ctrl or the Shift key on your keyboard and click the commits you wish to squash.
  6. Right-click the selected commits and select Squash commits.
  7. Modify the merge messages of the squashed commit if necessary and click Squash commits when prompted.
  8. Click Push origin in the top bar to push the changes to GitHub.

How to squash commits using Git CLI

Use Git to squash multiple commits from the command line as follows:

  1. Open a terminal and navigate to the root of the repository’s folder.

  2. Ensure you’re on the branch you wish to squash (some) of its commits by running the below command:

    git branch
    

    The selected branch is marked by an asterisk. Proceed to step 3 below if you’re on the correct branch. Or, run git switch YOUR-BRANCH-NAME to switch to the branch you want to change.

  3. Print a compressed version of the branch’s history to the screen by running the following command:

    git log --oneline
    
  4. Locate the oldest commit in the range of commits that you want to squash and copy the hash of the commit directly before (below) it. Note that the latest commit is shown first in the commit history.

    For example, if you want to squash the latest three documentation commits in the below example, copy the hash of the fix commit (i.e., a8f3604 fix: missing semi-colon (pesky eslint!) #4096) that is directly before (below) it.

    82af5ac (HEAD -> customize-template-4096) docs: paraphrase sentence #4096
    5c2195e docs: fix typo #4096
    73a05c3 docs: update README.md #4096
    a8f3604 fix: missing semi-colon (pesky eslint!) #4096
    852495f refactor: reduce login controller length #4096
    c2c9c96 feat: initial commit (definitely buggy) #4096
    
  5. Start the interactive rebase editor by running the below command. Replace COMMIT-HASH-YOU-COPIED with the commit hash you copied in the previous step.

    git rebase -i COMMIT-HASH-YOU-COPIED
    

    For example, if you want Git to merge all three docs commits in the above example into a single one, pass the hash (a8f3604) of the fix commit directly before (below) it to the git rebase command:

    git rebase -i a8f3604
    

    git rebase -i a8f360 instructs git to use commit a8f360 as the base or as the latest commit upon which the squashed commits will be placed. a8f360, in the above example, is essentially the latest snapshot of the branch before the squashed commits are added to the commit history.

  6. Skip the first line (oldest commit) of the rebase editor and replace the word pick with squash or s in subsequent lines that start with the word pick.

    The contents of the rebase editor of our example will look similar to the following after we complete this step:

    pick 73a05c3 docs: update README.md #4096                               
    s 5c2195e docs: fix typo #4096                                       
    s 82af5ac docs: paraphrase sentence #4096
    

    If your branch includes unrelated commits added after or in between the commits that you want to squash, do not replace pick with s or squash in the lines that hold the records of those commits. Doing otherwise will also squash those commits, whether related or not. The commit in the last line of the example below won’t change (i.e., pick a11a60d fix: mispelt command (pesky typos again!) #4096) if we only want to squash commits related to documentation changes.

    pick 73a05c3 docs: update README.md #4096                               
    s 5c2195e docs: fix typo #4096                                          
    s 82af5ac docs: paraphrase sentence #4096                               
    pick 6e9a7c1 feat: add development env config script #4096  
    

    The commit history will look similar to the below example after you run git rebase --continue:

    f670001 (HEAD -> customize-template-4096) feat: add development env config script #4096
    d126afd docs: update README.md #4096
    a8f3604 fix: missing semi-colon (pesky eslint!) #4096
    852495f refactor: reduce login controller length #4096
    c2c9c96 feat: initial commit (definitely buggy) #4096
    
  7. Merge the squashed commits into the branch’s history with the below command:

    git rebase --continue
    
  8. Modify and/or save the merge/commit messages for the squashed commits and close the merge/commit editor.

    The squashed commit history of our example will look like the following after committing the changes:

    08c4754 (HEAD -> customize-template-4096) docs: update README.md #4096
    a8f3604 fix: missing semi-colon (pesky eslint!) #4096
    852495f refactor: reduce login controller length #4096
    c2c9c96 feat: initial commit (definitely buggy) #4096
    
  9. Push the changes to GitHub

    git push origin BRANCH-NAME
    

    If you pushed the commits to GitHub before squashing them, you may need to pass the -f option or --force flag to the git push command.

    git push -f origin BRANCH-NAME
    

How to squash commits using Git Graph

Git Graph is a VSCode extension for managing Git repositories. It provides a graphical user interface that may be used to squash related commits in a local VSCode environment or a GitHub Codespace as follows:

  1. Install Git Graph in your Codespace or VSCode instance if it isn’t already installed.

  2. Click the Git Graph button on VSCode’s bottom pane.

  3. If the branch on which you want to squash commits isn’t checked out, check it out by right-clicking the branch’s name in the Graph field and selecting Checkout branch.

  4. Right-click the commit message previous to the oldest commit in the range of commits you want to squash. For example, if you want to squash the three commits highlighted by a purple rectangle in the image shown below, right-click the commit highlighted by the orange rectangle.

    git-graph-squash-base

  5. Select Rebase current branch to this commit.

  6. Tick the Launch interactive rebase in new terminal checkbox.

  7. Click Yes, rebase.

  8. Skip the first line (oldest commit) of the rebase editor and replace the word pick with squash or s in subsequent lines that start with the word pick.

    The contents of the rebase editor of our example will look similar to the following after we complete this step:

    pick 73a05c3 docs: update README.md #4096                               
    s 5c2195e docs: fix typo #4096                                       
    s 82af5ac docs: paraphrase sentence #4096
    

    If your branch includes unrelated commits added after or in between the commits that you want to squash, do not replace pick with s or squash in the lines that hold the records of those commits. Please see do not alter unrelated commits for more information.

  9. Click the source control button on VSCode’s side pane.

  10. If necessary, modify the commit message.

  11. Click Continue.

  12. Click Publish branch to push the commits to GitHub.

Further reading

Please refer to the following guides for additional Git collaboration guidance:

About Git