125

I'm very new to git, and was wondering if something like this is possible?

>git log --pretty=oneline --abbrev-commit
2f05aba Added new feature
3371cec Fixed screw up    <-- I want to remove this
daed25c Screw up          <-- and remove this.
e2b2a84 First.                So it's like they never happend.

Is this possible?

Josh
  • 6,046
  • 11
  • 52
  • 83
  • 1
    Note to future visitors: I think it's important to note that the answers to this question are split in their interpretation. Some assume "the commits never happened" and so the changes are not preserved ("drop"). Others assume the changes in the removed commits should be preserved ("squash" or "fixup"). Make sure to choose your answer based on which of those you need. – TTT May 22 '23 at 17:17

6 Answers6

110

If you truly wish to delete them (wiping them from history, never to be seen any more), you can

run rebase:

git rebase -i HEAD~4

and then, just delete (or comment out) the lines corresponding to the commits you wish to delete, like so:

pick 2f05aba ... #will be preserved
#pick 3371cec ... #will be deleted
#pick daed25c ... #will be deleted
pick e2b2a84 ... #will be preserved
Shathur
  • 1,425
  • 1
  • 15
  • 19
  • 2
    I'm new to github, How do you push the changes after that (when I click Sync, it just reverts what I did). I think I got it : `git push origin HEAD --force` – Nicolas Thery Aug 06 '14 at 12:12
  • @NicolasThery you don't. That's why git push refuses to work without --force, because you're re-writing history and you'll break other peoples repositories. If you've already pushed then your only option is to do a `git revert`. Which, when you think about it is sensible because if you pulled someone else's code you wouldn't want them to be able to erase your old commits without some kind of history would you? – Angry Dan Aug 29 '14 at 15:13
  • I assume this simply works in the current Git repo and does not change anything upstream/any other Git node? – Alexander Mills Apr 20 '16 at 07:17
  • 2
    @AlexanderMills It doesn't change anything upstream as long as you don't push it (and if you do, you have to add the --force flag as Angry Dan said). – Shathur Jan 18 '17 at 19:11
  • 2
    `d` or `drop` is the command to remove commits from the history. – Abandoned Cart May 18 '18 at 18:31
  • Commenting it out was instantaneous, while there were a lot of heavy files in that commit. Which makes me feel that the data is actually not getting removed from the local .git folder, is there some way to clean it up? – Daksh Shah Dec 26 '21 at 04:33
  • 1
    @NicolasThery Seeing as nobody is giving you an honest answer: the reason the Sync button doesn't work is because it does A LOT of garbage in the background to try 'restore' your branch to match your repo (it runs something like 15 individual git commands). If you're **sure** no one has cloned your repo, just run `git push --force` from the terminal. Else call it a day and accept the mistake. – aggregate1166877 May 10 '23 at 02:17
77

This is possible with git rebase. Try the following

git rebase -i HEAD~4

and then, follow the interactive instructions in your editor. In the first step you "squash" the commits. It should look something like this:

pick 2f05aba ... will be preserved
squash 3371cec ... will be squashed with daed25c
squash daed25c ... will be squashed with e2b2a84
pick e2b2a84 .. will contain this and 3371cec and daed25c

In the second step you can edit the commit messages.

h4ck3rm1k3
  • 2,060
  • 22
  • 33
Deve
  • 4,528
  • 2
  • 24
  • 27
  • 42
    Isn't the question about deleting the commits rather than squashing them? – Richard Apr 07 '13 at 01:24
  • 4
    @Stony: Well, you could probably also delete them. But from the commit comments in the OP ("Screw up" and "Fixed screw up") I assumed that the one is undoing the other and that you could as well just squash the two. – Deve Apr 07 '13 at 10:23
  • I'd say that's only your personal assumption. As @Richard mentioned, this is about deleting. – DarkTrick Sep 16 '22 at 12:11
20

To completely delete commit(s) there is a new option drop for git interactive rebases now. First run:

git rebase -i HEAD~4

Then replace pick with drop for the commit(s) to be dropped:

pick 2f05aba ... #will be preserved
drop 3371cec ... #will be dropped
drop daed25c ... #will be dropped
pick e2b2a84 ... #will be preserved

This worked on Fedora for me with the following version:

$ git version
git version 2.21.0
russoue
  • 5,180
  • 5
  • 27
  • 29
4

Squash is useful when you want to generate a list of merged commits under a single hash, but if you are looking to take three separate commits and have a final output that looks like it was a single commit, I recommend fixup

git rebase -i HEAD~4

pick 2f05aba ... will be preserved
fixup 3371cec ... will be merged with daed25c
fixup daed25c ... will be merged with e2b2a84
pick e2b2a84 .. will include 3371cec and daed25c, but only the commit message of e2b2a84

You can find more information in the command list of the interactive rebase UI.

Abandoned Cart
  • 4,512
  • 1
  • 34
  • 41
1

tl;dr: A one line command will work: git rebase 3371cec 2f05aba --onto e2b2a84

The Long:

Note the other answers here are split on the interpretation of the question. If you wish to preserve the changes in the commits you wish to remove, then you could use an interactive rebase (rebase -i) and then change "pick" for those 2 commits to "squash" or "fixup".

If you wish to remove the commits as if they never happened, without keeping their changes either, then you can also use interactive rebase and in the instructions list, change "pick" for the two commits to "drop", or comment out the lines, or delete the lines, all of which will do the same thing.

Note that if the one or more commits are grouped together, you can use a one-line rebase command without needing to bother with interactive mode. For example:

# Suppose you have: A-B-X-Y-C
# and you wish to remove X and Y:

git rebase Y C --onto B
# or (equivalent)
git rebase C~1 C --onto C~3
# both would result in: A-B-C'

# with this question's commit IDs:
git rebase 3371cec 2f05aba --onto e2b2a84
# or (equivalent)
git rebase 2f05aba~1 2f05aba --onto 2f05aba~3

Oftentimes I find myself just needing to remove a single commit from a feature branch's linear history, so assuming the commit ID to remove is X and the commit ID after X is K (the commit you wish to "keep"):

git rebase K~1 K --onto K~2

Note that K~2 is one plus the number of commits in a row that you wish to remove. If you wish to remove 2 commits you would use K~3, or 10 commits would be K~11.

TTT
  • 22,611
  • 8
  • 63
  • 69
0

Most of the time I face the same issue, i use below command to delete old commits and sync with server

git reset --hard HEAD~1
git push -f

Side Note: HEAD~1 will delete the last commit HEAD~5 will delete last 5 commits push -f is force push so that copies in server (github/bitbucket/other) is also deleted

Pavn
  • 160
  • 1
  • 10
  • The question wishes to keep a commit after removing other commits. So this answer is incorrect, as that commit would be lost. – TTT May 22 '23 at 17:19