0

Say I have a Git repo set up like this:

$ git init test && cd test
$ touch a && git add . && git commit -m "add a"
$ touch b && git add . && git commit -m "add b"
$ touch c && git add . && git commit -m "add c"
$
$ git --no-pager log --pretty="%h %s" # print short SHA and message subject
176d6d0 add c
495991e add b
1d8444a add a

Now for some reasons, I want to squash the most recent 2 commits (176d6d0 add c and 495991e add b) into one [1]:

$ git reset --soft 1d8444a # go back to the first commit ("add a")
$ git add . && git commit -m "bc"
$
$ git --no-pager log --pretty="%h %s"
c01e64a bc
1d8444a add a

Yeah, it looks we are done!

Not quite!

If we look at git reflog show, the old commits are still around:

$ git --no-pager reflog show
c01e64a (HEAD -> master) HEAD@{0}: commit: bc
1d8444a HEAD@{1}: reset: moving to 1d8444a9747bd5e4176c
176d6d0 HEAD@{2}: commit: add c                           # old commit
495991e HEAD@{3}: commit: add b                           # old commit
1d8444a HEAD@{4}: commit (initial): add a

If we checkout an old commit, we can still restore it.

$ git --no-pager show -s 176d6d0 # this is the "add c" commit
commit 176d6d0aa92b230f4dd1e4ed8ae028b7c5fbb2d5
Author: XXX <XXX@XXX.com>
Date:   Tue Oct 22 22:19:23 2019 -0400

    add c

$ git checkout 176d6d0
Note: checking out '176d6d0'.

You are in 'detached HEAD' state. You can look around
... # omitted

HEAD is now at 176d6d0 add c

I tried to delete these stale old commits, but none of these works, the old commits are still visible in git reflog show and git checkout-able:

git gc
git gc --prune=all
git reflog expire --all
git reflog expire --expire=now
git reflog expire --expire-unreachable=now
git reflog expire --expire=now --all # tricky, see update below

Questions:

  1. How should I really discard the old commits (176d6d0 add c and 495991e add b)?
  2. What's the jargon for these old commits (176d6d0 add c and 495991e add b), e.g. are they really "unreachable commits", "stale commits"?
    What's the jargon of the act that removes them, e.g. is it "prune", "garbage collection"?
  3. People do git reset or git rebase -i all the time. If they don't somehow remove these stale old commits, will they get a bloating Git repo, especially for LARGE projects with lots of collaborators (like Chromium)?
  4. (nice to have) Please explain why your answer to 1 works.

footnotes:

[1] I could use git rebase -i but I want to do it programmatically instead of interactively so that it can be automated by a script, but that's beside the point)

[2] this answer does not quite answer my questions

Update:

This removes all entries showed by git reflog show, but I don't quite understand why from reading the docs.. and other questions remain (plz see above)

    git reflog expire --expire=now --all

However, old commits, though not shown in git reflog show, are still git checkout-able if you remember the SHA, that means these old commits are not really deleted!!

So this is not the answer.

Leedehai
  • 3,660
  • 3
  • 21
  • 44
  • Actually your question *is* answered over there, at least in the comments: "Besides reflog entries (which keep objects alive until the reflog entry itself is deleted), all objects get a grace period, 14 days by default [...] `--prune-all`" – o11c Oct 23 '19 at 03:02
  • @o11c I tried that, `git gc --prune=all` doesn't delete them.. (added to the list of commands that don't work) – Leedehai Oct 23 '19 at 03:11
  • 1
    You don't need to do the add after the soft reset, your index entries all still point the same places (soft reset doesn't touch the index) – jthill Oct 23 '19 at 03:46

1 Answers1

3

You need to expire the reflogs and then prune those commits:

git reflog expire --expire=now --all
git gc --prune=now

As you've figured out, running any one of the above commands doesn't work. You need to run both in order.

But usually you shouldn't need to do this manually - git excels at maintaining those records and manually cleaning them up doesn't add much benefits.

iBug
  • 35,554
  • 7
  • 89
  • 134