245

I would like to remove selected commit log entries from a linear commit tree, so that the entries do not show in the commit log.

My commit tree looks something like:

R--A--B--C--D--E--HEAD

I would like to remove the B and C entries so that they do not show in the commit log, but changes from A to D should be preserved. Maybe by introducing a single commit, so that B and C become BC and the tree looks like.

R--A--BC--D--E--HEAD

Or, ideally, after A comes D directly. D' representing changes from A to B, B to C and C to D.

R--A--D'--E--HEAD

Is this possible? if yes, how?

This is a fairly new project so has no branches as of now, hence no merges as well.

xk0der
  • 3,600
  • 4
  • 24
  • 35
  • @xk0der: "commits" is the right term here. `rebase` may remove old/create new commits. I don't know what "commit log entries" means. – jfs Aug 11 '12 at 11:44
  • @J.F.Sebastian I don't see a problem with "commit log" - Log of all the commits. And I wanted to delete a few entries from the log - while keeping the actual changes (the commits). – xk0der Aug 12 '12 at 13:20
  • @xk0der: [git commits are content-addressable](http://git-scm.com/book/ch9-2.html) i.e., if you change *anything* in a commit e.g., its log message; you create a new commit. You could [read git's commit without git and see for yourself](https://gist.github.com/754cdf6540f8ed7c4e56). – jfs Aug 12 '12 at 15:18
  • @J.F.Sebastian - Thanks for the links - I know that - But does that technicality really change the problem I was facing and how I put it forth? I guess not. In the end: I wanted to remove "the commit log messages" - without removing the "commit changes" - Please reread my question - specially the second paragraph. To add more `git log` shows the "commit log" http://git-scm.com/docs/git-log . And I wanted to get rid of two entries from that log - not the changes. – xk0der Aug 12 '12 at 17:25

9 Answers9

276

git-rebase(1) does exactly that.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] contains an example.

  1. Don't use git-rebase on public (remote) commits.
  2. Make sure your working directory is clean (commit or stash your current changes).
  3. Run the above command. It launches your $EDITOR.
  4. Replace pick before C and D by squash. It will meld C and D into B. If you want to delete a commit then just delete its line.

If you are lost, type:

$ git rebase --abort  
jfs
  • 346,887
  • 152
  • 868
  • 1,518
  • Thanks for the quick reply. So do I checkout A and do a rebase, something like `git rebase -i D [A]`? – xk0der Jan 30 '09 at 12:41
  • 6
    @slhck: http://replay.web.archive.org/20090627051320/http://blog.madism.org/index.php/2007/09/09/138-git-awsome-ness-git-rebase-interactive – Benjol May 19 '11 at 09:58
  • 3
    How can we do it on remote repos ? – Eray Nov 18 '11 at 02:28
  • 7
    @Eray: just `push -f` your changes. Don't do it if you're not working alone. – jfs Nov 18 '11 at 19:37
  • @nalply: [If you do, people will hate you, and you’ll be scorned by friends and family.](http://progit.org/book/ch3-6.html#the_perils_of_rebasing) – jfs Dec 04 '11 at 12:36
  • @J.F.Sebastian, thanks this makes sense. If we are a small team could we stop the world: everybody push, rebase, everybody delete local and clone? Would this work? – nalply Dec 05 '11 at 20:23
  • Updated link to use wayback machine. – ripper234 Dec 31 '11 at 09:21
  • 2
    @ripper234: I've fixed links to point `git-rebase` manual and wayback machine for the blog post. – jfs Feb 28 '12 at 12:57
  • git rebase -i HEAD~5 followed by :q (from vim) results in --no-ff "Merge from..." commits being removed. – billrichards Apr 04 '14 at 03:38
  • `@~5` is a shortcut for `HEAD~5` – Filip Bartuzi Aug 26 '15 at 15:37
  • @J.F.Sebastian I am genuinely wanting to delete a commit and all its data/changes, but when I remove the commit line that I want to delete and exit the $EDITOR with `:wq` all I get is a message that says `could not execute editor`. And them when I check under `git log` the commit I wanted to delete is still there. What does that error mean? – Maggie S. Mar 05 '16 at 18:24
  • @MaggieB.: these questions: "how to delete a line in my $EDITOR, how to save the changes to disk, how to exit the editor, how to specify another editor that you are more familiar with" should be asked separately. The answers depends on your `$EDITOR` (don't forget to mention what it is). See [ask] – jfs Mar 05 '16 at 19:00
  • Just to echo what @jfs said and add some more context, doing a rebase *will rewrite the log shas*! This is important to note, because if you've already pushed up, you'll need to force push to overwrite the upstream log. Problem there, as mentioned, anyone else working off that branch now can't push since the origin has changed. `git push --force-with-lease` is one thing to make note of, but in general, DON'T FORCE PUSH UNLESS YOU'RE WORKING ALONE! – seeafish Jun 05 '18 at 16:08
76
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
  • 648,528
  • 94
  • 608
  • 638
  • This works too and helped me to understand what a soft reset is. Granted, the "top" answer is right too and shorter, but thanks for this answer as well. – cgp Apr 26 '12 at 15:37
41

Here is a way to remove a specific commit id knowing only the commit id you would like to remove.

git rebase --onto commit-id^ commit-id

Note that this actually removes the change that was introduced by the commit.

alex
  • 438,662
  • 188
  • 837
  • 957
rado
  • 3,830
  • 3
  • 28
  • 24
  • 7
    The extra HEAD in this command is will cause the rebase to finish with a 'detached HEAD' which is undesirable. It should be ommitted. – Frosty May 27 '11 at 12:45
  • 3
    This reverts the changes introduced my commit-id, the OP wants to retain the changes, just squash the commits. – CB Bailey Sep 24 '11 at 07:28
  • 1
    -1 because it doesn't do what the OP asked (rather it destroys something he explicitly wanted to retain). – Emil Styrke Mar 28 '13 at 09:15
  • 1
    Although it doesn't do what the OP asked for, it was exactly what I needed, so +1 for a useful answer. – Edvins Jul 30 '19 at 11:27
20

To expand on J.F. Sebastian's answer:

You can use git-rebase to easily make all kinds of changes to your commit history.

After running git rebase --interactive you get the following in your $EDITOR:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

You can move lines to change the order of commits and delete lines to remove that commit. Or you can add a command to combine (squash) two commits into a single commit (previous commit is the above commit), edit commits (what was changed), or reword commit messages.

I think pick just means that you want to leave that commit alone.

(Example is from here)

Community
  • 1
  • 1
idbrii
  • 9,440
  • 5
  • 50
  • 93
14

You can non-interactively remove B and C in your example with:

git rebase --onto HEAD~5 HEAD~3 HEAD

or symbolically,

git rebase --onto A C HEAD

Note that the changes in B and C will not be in D; they will be gone.

seh
  • 14,430
  • 2
  • 48
  • 57
Head
  • 4,391
  • 3
  • 28
  • 18
  • See here for more info: https://sethrobertson.github.io/GitFixUm/fixup.html#remove_deep – Max Apr 28 '17 at 07:24
3

One more way,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

pick the hash that you want to use it as a base, and the above command should make it interactive so you can squash all the top messages ( you need to leave the oldest )

resultsway
  • 8,938
  • 5
  • 29
  • 38
2

I find this process much safer and easier to understand by creating another branch from the SHA1 of A and cherry-picking the desired changes so I can make sure I'm satisfied with how this new branch looks. After that, it is easy to remove the old branch and rename the new one.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Eric Woodruff
  • 5,907
  • 3
  • 31
  • 32
  • if you do this you will lose the E commit as well, won't you? As I understood, you delete master and rename rework as master (considering the ABCDE flow is the master branch). – Renan Bandeira Sep 14 '16 at 14:26
1

Just collected all people's answers:(m new to git plz use it for reference only)

git rebase to delete any commits

git log

-first check from which commit you want to rebase

git rebase -i HEAD~1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

Then the screen will look something like this:

pick 0c2236d Added new line.

Rebase 2a1cd65..0c2236d onto 2a1cd65 (1 command)

#

Commands:

p, pick = use commit

r, reword = use commit, but edit the commit message

e, edit = use commit, but stop for amending

s, squash = use commit, but meld into previous commit

f, fixup = like "squash", but discard this commit's log message

x, exec = run command (the rest of the line) using shell

d, drop = remove commit

#

These lines can be re-ordered; they are executed from top to bottom.

#

If you remove a line here THAT COMMIT WILL BE LOST.

#

However, if you remove everything, the rebase will be aborted.

#

Note that empty commits are commented out ~ ~

~
~
~
~
~
~
~
~
~

Here change the first line as per your need (using the commands listed above i.e. 'drop' to remove commit etc.) Once done the editing press ':x' to save and exit editor(this is for vim editor only)

And then

git push

If its showing problem then you need to forcefully push the changes to remote(ITS VERY CRITICAL : dont force push if you are working in team)

git push -f origin

-1

You can use git cherry-pick for this. 'cherry-pick' will apply a commit onto the branch your on now.

then do

git rebase --hard <SHA1 of A>

then apply the D and E commits.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

This will skip out the B and C commit. Having said that it might be impossible to apply the D commit to the branch without B, so YMMV.

Rory
  • 48,706
  • 67
  • 174
  • 234