21

A developer recently left who left a ton of commits in a repo from a few months ago that are just like 'updated'. Ideally, I'd like to squash them into a single commit but I have only done this for recently commits.

How would I do it for like the following commits (assuming the from 2 months ago means that there are hundreds of these)?

.... from 2 months ago

aabbcc updated
aabbdd updated
aabbee updated
aabbff updated

Not wanting / needing anything fancy, just a simple solution. These commits haven't been publicly shared (other than with me today) so no issue of upsetting other people's commit history.

3 Answers 3

10

In order to do a git squash follow those steps:

// X is the number of commits you wish to squash
git rebase -i HEAD~X

Once you squash your commits - choose the s for squash = it will combine all the commits into a single commit.

enter image description here


You also have the --root flag in case you need it

try: git rebase -i --root

--root

Rebase all commits reachable from <branch>, instead of limiting them with
an <upstream>.

This allows you to rebase the root commit(s) on a branch.  
When used with --onto, it will skip changes already contained in `<newbase>`   
(instead of `<upstream>`) whereas without --onto it will operate on every 
change. When used together with both --onto and --preserve-merges, all root 
commits will be rewritten to have `<newbase>` as parent instead.`
10
  • 1
    I haven't - is there any way to pass rebase -i a range of commit ids like aabbcc..aabbbff
    – timpone
    Commented Jan 12, 2016 at 0:08
  • @timpone No. Interactive rebase is a script that uses git cherry-pick under the hood. With cherry-pick you can pick a range.
    – Kaz
    Commented Jan 12, 2016 at 0:08
  • Nope, you cannot use range
    – CodeWizard
    Commented Jan 12, 2016 at 0:09
  • 1
    shoot - a dumb ? but would it be possible to checkout at a specific point and then do something like git rebase -i HEAD~200? or would it be safer to just let this be which I'm totally fine with
    – timpone
    Commented Jan 12, 2016 at 0:17
  • 1
    Cool. let me know if you need any more help regrading this issue :-)
    – CodeWizard
    Commented Jan 12, 2016 at 5:26
5

I know that this is already an ancient question but I needed a solution for this.

Long story short, my local git repo (on NFS, no upstream) works as a back up to certain files and I wanted it to have a maximum of 50 commits. Since there are many files and the backups are taken rather often, I needed something that automatically squashes the history, so I created a script that both backs the files up and squashes the history.

#!/bin/bash

# Max number of commits preserved
MAX_COMMITS=50

# First commit (HEAD~<number>) to be squashed
FIRST_SQUASH=$(echo "${MAX_COMMITS}-1"|bc)

# Number of commits until squash
SQUASH_LIMIT=60

# Date and time for commit message
DATE=$(date +'%F %R')

# Number of current commits
CURRENT_COMMITS=$(git log --oneline|wc -l)

if [ "${CURRENT_COMMITS}" -gt "${SQUASH_LIMIT}" ]; then

    # Checkout a new branch 'temp' with the first commit to be squashed
    git checkout -b temp HEAD~${FIRST_SQUASH}

    # Reset (soft) to the very first commit in history
    git reset $(git rev-list --max-parents=0 --abbrev-commit HEAD)

    # Add and commit (--amend) all the files
    git add -A
    git commit --amend -m "Automatic squash on ${DATE}"

    # Cherry pick all the non-squashed commits from 'master'
    git cherry-pick master~${FIRST_SQUASH}..master

    # Delete the 'master' branch and rename the 'temp' to 'master'
    git branch -D master
    git branch -m master

fi

So, what the script basically does is (I removed the back up part):

  1. If there are more than 60 commits, it squashes all the commits from 50 to 60+ into a one commit.
  2. It creates and checks out a new branch based on the commit
  3. Cherry picks the remaining commits from the master (#1 to #49) to the branch
  4. Removes the master branch
  5. Renames the new branch to master.
3
  • this should be good choice for tracking binary files, but i got twice the size in .git folder with this approach. trying stuff here stackoverflow.com/questions/3797907/… wont get me significant smaller .git size. Is it because it uses cherry pick and remove branch instead of squashing?
    – izzulmakin
    Commented Dec 24, 2020 at 4:38
  • 1
    it's getting bigger because it uses git add -A, and i manually copied .git into .gitbk and that folder which obviously not in .gitignore, got added as well! in my case i edited that line into git add `git ls-tree --full-tree -r --name-only HEAD` and added git gc --aggressive
    – izzulmakin
    Commented Dec 24, 2020 at 5:57
  • 2
    bc isn't always present. You can use built-in bash arithmetic to add a bit more portability: FIRST_SQUASH=$(expr $MAX_COMMITS - 1) Commented Dec 25, 2020 at 22:25
4

The age of the commits doesn't matter, squashing commits is squashing commits.

If rebasing isn't preferable for you, or there are literally thousands of commits that you want to squash and can't be bothered with it, you could just reset softly to the first commit hash and re-commit everything:

$ git reset aabbff 
$ git commit -m "This commit now contains everything from the tip until aabbff"

You would then only have one commit, the same as rebase -> squash.

3
  • haha, it's not thousands but like 200 and they are from a period where he just everything a commit. Is there a way to reset from aabbff to another specific commit (like the reverse of the tilde character I think). Sorry, I'm definitely not a git guru so my biggest fear is to muck things up.
    – timpone
    Commented Jan 12, 2016 at 0:16
  • Hey @timpone - no, the reset command will reset from the tip of the branch back to the revision/hash you specify. If you want to target a range of commits somewhere in the middle of the branch history, you should rebase.
    – scrowler
    Commented Jan 12, 2016 at 0:24
  • Just to add additional explanation, the commit hash should be hash of the one commit before the commit from which you want to squash the changes
    – Aleks
    Commented Aug 24, 2021 at 12:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.