Search code examples
gitrebasecollaborationsquashgit-squash

What is the best practice to squash pushed Git commits


So far, I have been doing interactive git rebasing fairly often on either: unpushed work, or on pushed work that I knew for sure I was the only one to work on.

I know squashing commits and overall rewriting history can be tricky when it gets to collaborative work.

What is the best practice to do it with as little pain as possible?

If I squash some commits and push, how will the repo look like on a colleague's station when he pulls?

  1. When he has done no change and just needs to update?
  2. When he did do some changes, and wants the update before committing himself?

I know that the very concept of rewriting history is a never-ending debate, but here I am just looking for a technical solution to handle this.


Solution

  • What is the best practice to do it with as little pain as possible?

    The best practice is to avoid it.

    As ElpieKay suggested, it's a good practice to have a "release branch", where the history is safe and things are stable, but also to have "development/feature branches" where you can do whatever you want until you merge/rebase them into your stable master branch again.

    To answer your questions:

    1. When he has done no change and just needs to update?

    When didn't change anything and pulls, git will take the remote's history as the correct one (because someone obviously --force-pushed, so it has to be right.) So it will just overwrite your collaborators work and make the commit history match the history on the remote server. It will show this with an additional message like force update just as it does when you force push something.

    Note that the old commits won't really be deleted - Just "hidden", as they are unlinked from the rest of the commit graph. So if you suddenly needed access again to the "deleted" commits, that would maybe be possible as long as you have the SHA hash of the lost commits somewhere. The git garbage cleaner deletes unreachable objects after a certain amount of operations on the repository.

    1. When he did do some changes, and wants the update before committing himself?

    That's the worst case obviously. Anyway, make sure you have a backup copy of all your changes and your branch by committing your changes (even if you want to apply them afterwards!), creating a temporary, new one

    git add . ; git commit -am "Describe all my changes here" ; git branch backup
    

    Now that your changes are safely stored in the backup branch, you can remove the commit again.

    git reset --hard HEAD~
    

    Then pull the current branch

    git pull
    

    This should work without any merge conflicts as it basically represents case #1, where no changes have been made and it will just make the local history match the remote one. Now you might be able to apply your changes again, by applying your changes ( = the latest commit on the backup branch) again.

    git cherry-pick backup
    

    Obviously, this might lead to a merge conflict, when the remote repository updated the same lines of the code. If there were no conflicts then you can delete the backup branch. Your repository is up-to-date and your changes are applied on top of it.

    But again, try to avoid these situations. Either squash/rebase in a separate feature branch before merging or let the commits be as they are after they are merged.