Skip to content

Instantly share code, notes, and snippets.

@aortbals
Last active December 2, 2024 20:08
Show Gist options
  • Save aortbals/2aeb557bf127dd7ae88ea63da93479fc to your computer and use it in GitHub Desktop.
Save aortbals/2aeb557bf127dd7ae88ea63da93479fc to your computer and use it in GitHub Desktop.
Squash and Merge on the Command line

With the introduction of GitHub's Squash and Merge feature, this has become less prevelant, however it's still useful in scenarios where GitHub's interface is unavailable.

Let's talk through two ways to do a squash and merge on the command line.

Take 1: Interactive Rebase

When to use it

  • When you have not merged main into your feature branch
  • There are no merge conflicts
  • When you want to retain the original committer on the squashed commit

Steps

You are working on branch feat-fuu. You want to create a single squashed commit to push to your remote feat-fuu branch on GitHub.

  1. git checkout main
  2. git pull
  3. git checkout feat-fuu
  4. git checkout feat-fuu-backup
    • Optional but recommended - make a backup version of your branch
  5. git checkout feat-fuu
  6. EDITOR='code -w' git rebase -i main
    • Setting EDITOR is optional, and depends on your editor of choice. With the case of VSCode or Sublime Text, the -w flag tells the editor to "wait" until the file exits before closing.
  7. In your editor, edit all of the additional commits to squash. Leave the first commit in the list alone
  8. Save and exit your editor
  9. Rewrite a nice single commit message for the commit
  10. Check the history. feat-fuu will now contain a single commmit ahead of main
  11. git push -f origin feat-fuu
    • Please be careful with this step, as it overwrites your original remote branch on GitHub

Take 2: Git Squashed Merge

When to use it

  • You have merged main into your branch and resolved conflicts
  • You don't care about the author of the original commits (you will be rewriting it)

You are working on branch feat-fuu. You want to create a single squashed commit to push to your remote feat-fuu branch on GitHub.

  1. git checkout main
  2. git pull
  3. git checkout feat-fuu
  4. git checkout feat-fuu-backup
  5. git checkout main
  6. git branch -D feat-fuu
    • You are deleting your original branch. Ensure you have created feat-fuu-backup beforehand and it has your full commit history.
  7. git checkout -b feat-fuu
    • This creates a fresh branch from main
  8. git merge --squash feat-fuu-backup
    • You are merging and squashing your original work into a single commit. This is where the magic happens.
  9. Rewrite a nice single commit message for the commit
  10. Check the history. You should see a single commmit on your branch that branched from main
  11. git push -f -u origin feat-fuu
    • Please be careful with this step, as it overwrites your original branch on GitHub

References

  • This Stack Overflow Post has additional detail about the differences between these two approaches.
@user24
Copy link

user24 commented Mar 30, 2022

Would you consider updating these instructions to replace references to master with main as that is the standard main branch name now?

@aortbals
Copy link
Author

Would you consider updating these instructions to replace references to master with main as that is the standard main branch name now?

Great idea. Will do that.

@user24
Copy link

user24 commented Mar 30, 2022

Would you consider updating these instructions to replace references to master with main as that is the standard main branch name now?

Great idea. Will do that.

Awesome 😊

@matu3ba
Copy link

matu3ba commented Dec 2, 2024

I find it very annoying to being forced to switch branches for 1. merge commit and 2. squashed merge and there is no reason why rebase could not have a --squash option or merge have an --onto option.
The same applies to rev-parse and alike options.
What works:

# currently on branch 'feature' without git switch/checkout
# from branch 'feature' to branch 'master'
# -1 push master to upstream/master
#git push <remote> <local_branch_name>:<remote_branch_to_push_into>
git push upstream master:master
# 0. fast-forward merge
#git fetch <remote> <sourceBranch>:<destinationBranch>
git fetch . feature:master
# 1. merge with merge commit + push
git switch master && git merge --no-ff --no-commit && git switch feature
git switch master && git merge --no-ff --no-commit && git switch feature && git push upstream master:master
# 2. quash and merge + push
git switch master && git merge --squash && git switch feature
git switch master && git merge --squash && git switch feature && git push upstream master:master
# 3. rebase and merge + push
git rebase master feature && git fetch . feature:master
git rebase master feature && git fetch . feature:master && git push upstream master:master

@matu3ba
Copy link

matu3ba commented Dec 2, 2024

Since the writing of this guide many things have been upated:

    1. pull should be always done with --ff-only or being configured to prevent accidental messups
    1. checkout is only necessary for orphaned or loose commits, otherwise use switch
    1. EDITOR should be set as environment variable, no need for -w
    1. git push force should never be used, use git push --force-if-includes --force-with-lease or at least --force-with-lease to prevent force push over another force push by your colleague

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment