141

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?

1
  • 6
    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. Commented May 22, 2023 at 17:17

5 Answers 5

123

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 
Sign up to request clarification or add additional context in comments.

7 Comments

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
@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?
@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).
d or drop is the command to remove commits from the history.
@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.
|
83

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.

4 Comments

Isn't the question about deleting the commits rather than squashing them?
@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.
I'd say that's only your personal assumption. As @Richard mentioned, this is about deleting.
The list is reversed. squash... commits will be merged with their parents. parents are above their children in this rebase list (reversed ordering than in other git tools). Also, commands are executed from top. So it will be 3371cec merging with 2f05aba and then daed25c merging with something we don't know the hash of, as 2f05aba will be changed by the first merge and 3371cec will not be a parent of daed25c anymore. But I agree this is what OP wanted. Should be said though, this is not called delete or remove in git terminology.
25

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 

Comments

9

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 2f05aba (message lost) fixup daed25c ... will be merged with 3371cec (message lost) pick e2b2a84 .. will be preserved 

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

4 Comments

The first sentence implies fixup does something else with commits itself than squash. But they are the same, except fixup does not ask what to do with a commit message.
@papo It combines (or squashes, if using that doesn't confuse you) multiple commits and discards the messages of the fixup commits. I think you're confusing the fact that they're different commands with claiming they can't be similar at all.
I commented because the sentence made me uncertain and I went to check docs and do tests. It turned out it was a waste of time. It is indeed doing the same thing with those commits. First part of the sentence also applies to fixup. Second part applies to squash too. Confusing is the "but" between. Also, comment "will be merged with 3371cec" is not correct. At the point daed25c is processed, 3371cec will not exist. As well as 2f05aba. I didn't mean to correct you by that first comment, just add some info and save time for other confused readers.
@papo I guess one person reading too far into it 7 years later isn’t worth an edit, but thanks for the added clarification.
2

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

The Long:

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.

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.