You can do it the right way. But then again, you might not want to. After all, it's a stupid ass pain: modal, raises non-existent merge conflicts, knows better than you do, never heard of user control, wants you to use vim or emacs.
Why do it?
Information hiding. What's 21 commits for me should be an atomic change for the rest of the team.
Since git doesn't have a concept of private/public commits, or whatever non-destructive solution you might come up with, we are stuck with a revisionist, unsafe, destructive approach to collaboration.
Before we start
At some point you may wish to revert local changes. Sadly "revert local changes" can mean a whole bunch of stuff from git's revisionist perspective. See here.
We are trying to collapse a bunch of commits into a single, unique commit, so, in this case, "revert local changes" maps to:
git reset --hard origin/master
This will reset clean to your latest remote.
Hint: if you have been rebasing over and over while working on your branch, the number of commits you want to squash is less than the number of commits on your branch. Check the commit history to distinguish your commits from others'.
Squashing without squashing
I'm assuming that all your changes are already committed. One of the methods explained here shows how to non-interactively 'squash' N commits:
git reset --soft HEAD~N
git commit -m "shiny new commit"
git push -f
Where "N" is the number of commits to squash. Done 21 commits, want a single commit instead? N = 21.
Tip: calling reset HEAD~1 twice is equivalent to reset HEAD~2.
I gather that...
- "git reset --soft" rolls back our commit number to 'N before now' but without actually changing local files.
- "git commit..." commits changes locally, with the specified commit message.
- "git push -f" (so called force push) tells the server to discard previous changes, replacing them with the latest local commit.
No love lost on the git workflow.