I use git to backup and restore folders that can change on my system and they can get really large as useless history accumulates. I only need the last 4 or so commits, how can I squash or delete the whole history except for the last n commits?
-
3You can't. Really! What you *can* do is throw away *all* the commits and make N *new* commits that represent your truncated history. (Or, you can make a *shallow clone*, but those have certain limitations, so that's probably not the way to go, plus you're still duplicating everything.) But Git makes a terrible backup system; don't use Git as a backup system. – torek Jul 15 '22 at 14:27
-
What would you recommend? These are actually for video game save files haha, using git is the fastest method I have found so far, much faster than copying and pasting and renaming folders around. Because I am a bit of a cheat (at the moment) and frequently restore for rerolling RNG and having a command I can run in the terminal is great. Maybe I should write my own scritps for copying/pasting to a backups directory? – Tachytaenius Jul 15 '22 at 17:17
-
I'm on a mac so I just use Time Machine myself. – torek Jul 15 '22 at 17:41
-
I guess if I am too lazy to come up with or find anything right now I can just delete .git whenever I'm certain I'm safe – Tachytaenius Jul 15 '22 at 18:47
1 Answers
Disclaimer: Your first sentence may have unfortunately tainted this question:
I use git to backup and restore folders that can change on my system and they can get really large as useless history accumulates.
If the history is "useless", than perhaps Git isn't the correct tool to use. As mentioned in the comments, Git is generally not the right tool for a backup system. That being said, if your question didn't include the first sentence, we wouldn't have been able to pass judgement as to "why" you wish to do this, and your remaining question is certainly valid:
I only need the last 4 or so commits, how can I squash or delete the whole history except for the last n commits?
Here's one fairly straight-forward way to accomplish this. Note this method requires that you determine some things before you begin:
- The branch name you wish to rewrite. (This answer will assume it is called
main
.) - The root commit of your branch. Get it with:
git log main --reverse
(This answer will assume it is called<old-repo-root-commit-id>
.) - The oldest commit ID you want to keep, e.g. your forth commit from the top, which will become your new repo root commit. (This answer will assume it is called
<new-repo-root-commit-id>
.) - Your
git status
should be clean before you start. If it isn't, consider committing (or undoing) your most recent changes.
Here are the set of commands to run:
git switch --detach <new-repo-root-commit-id>
git reset --soft <old-repo-root-commit-id>
git commit --amend --reuse-message=<new-repo-root-commit-id>
git rebase <new-repo-root-commit-id> main --onto @
I see your title mentions your goal is to save space. In order for this to also achieve that goal you can't have any other refs leftover in your repo which point to your old commit IDs. If you only have a single branch in your system without any tags, then the old commits will eventually get garbage collected. If you want to clean it up right now, see this question.
Detailed explanation of how the commands work:
- The
switch
command simply checks out the specific commit you want to be your new root commit. This is identical togit checkout <new-repo-root-commit-id>
but the newerswitch
command requires you specify--detach
when you are checking out a commit ID rather than a named branch. - The
reset
command says to change which commit you're currently pointing to, to instead be the root commit ID, and the--soft
says to not change your local files, and also leave all the changes that would happen as a result of this reset, as staged and ready to be committed. - Note after the reset you are pointing to the very first commit in your repo, and now you are going to amend (rewrite) that commit to include all changes between the root and the new root commit. This is essentially just squashing all the previous commits into a single commit. The
--reuse-message
option says to use the commit message from the new repo root instead of the original repo root. This isn't actually needed, but it's more likely that the more recent commit message is better than the original one. This of course depends on what your commit messages say; when committing you can set a message from any other commit if you wish, or create a new different one too. - After the commit command you have exactly one commit, and now you will
rebase
(replay) the remaining (e.g. 3) commits that were onmain
, "onto" your new commit, leaving you with just 4 commits. At this point if you do a diff ofmain
with the commit ID thatmain
was on before this process, the results should be empty, because you didn't actually change your working files at all. (Note you can usegit reflog
to see what commit IDmain
was on before you did this, as long as you didn't perform a full garbage collection yet of reflogs and old commits.)

- 22,611
- 8
- 63
- 69
-
Bizarrely, in windows powershell, the last command does not work due to the @ syntax. But running cmd within powershell lets it get through. But anyway, it works! Very cool, thank you! – Tachytaenius Jul 24 '22 at 15:12
-
1@Tachytaenius `@` is just shorthand for `HEAD` so you can substitute that anywhere you see it. I guess in powershell you might need `@ . – TTT Jul 24 '22 at 20:42
-
-
1@Jun are you asking in the general sense, how do other users update their branches after a shared branch has been rewritten? There are a few different ways to go, but generally `git fetch` to update the remotes, and for shared branches, then either reset hard to the remote, or simply delete the branches and re-check them out again. For WIP feature branches, generally `git rebase --onto` will probably come in handy, and I always suggest that the person rewriting a shared branch coordinate with the entire team and provide instructions for how to update feature branches. – TTT Feb 04 '23 at 01:03
-