How to remove selected commit log entries from a Git repository while keeping their changes?

218,652

Solution 1

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  

Solution 2

# 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

Solution 3

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.

Solution 4

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)

Solution 5

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.

Share:
218,652

Related videos on Youtube

xk0der
Author by

xk0der

I love coding, spending time with my wife and daughter, and reading programming related books and articles.

Updated on October 16, 2020

Comments

  • xk0der
    xk0der over 3 years

    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.

    • jfs
      jfs almost 12 years
      @xk0der: "commits" is the right term here. rebase may remove old/create new commits. I don't know what "commit log entries" means.
    • xk0der
      xk0der almost 12 years
      @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).
    • jfs
      jfs almost 12 years
      @xk0der: git commits are content-addressable 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.
    • xk0der
      xk0der almost 12 years
      @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" git-scm.com/docs/git-log . And I wanted to get rid of two entries from that log - not the changes.
  • xk0der
    xk0der over 15 years
    Thanks for the quick reply. So do I checkout A and do a rebase, something like git rebase -i D [A]?
  • jfs
    jfs almost 15 years
    The OP wants to combine B,C,D commits, not to delete their changes.
  • tina Miller
    tina Miller about 13 years
  • Frosty
    Frosty about 13 years
    The extra HEAD in this command is will cause the rebase to finish with a 'detached HEAD' which is undesirable. It should be ommitted.
  • CB Bailey
    CB Bailey almost 13 years
    This reverts the changes introduced my commit-id, the OP wants to retain the changes, just squash the commits.
  • Eray
    Eray over 12 years
    How can we do it on remote repos ?
  • jfs
    jfs over 12 years
    @Eray: just push -f your changes. Don't do it if you're not working alone.
  • jfs
    jfs over 12 years
  • nalply
    nalply over 12 years
    @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?
  • ripper234
    ripper234 over 12 years
    Updated link to use wayback machine.
  • Mauricio Scheffer
    Mauricio Scheffer over 12 years
    I think you meant reset --hard, not rebase --hard (which doesn't exist)
  • jfs
    jfs over 12 years
    @ripper234: I've fixed links to point git-rebase manual and wayback machine for the blog post.
  • cgp
    cgp about 12 years
    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.
  • Emil Styrke
    Emil Styrke over 11 years
    -1 because it doesn't do what the OP asked (rather it destroys something he explicitly wanted to retain).
  • billrichards
    billrichards about 10 years
    <funfact> git rebase -i HEAD~5 followed by :q (from vim) results in --no-ff "Merge from..." commits being removed. </funfact>
  • Filip Bartuzi
    Filip Bartuzi almost 9 years
    @~5 is a shortcut for HEAD~5
  • Maggie S.
    Maggie S. over 8 years
    @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?
  • jfs
    jfs over 8 years
    @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 How to Ask
  • Renan Bandeira
    Renan Bandeira almost 8 years
    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).
  • Max
    Max about 7 years
  • seeafish
    seeafish about 6 years
    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!
  • Edvins
    Edvins almost 5 years
    Although it doesn't do what the OP asked for, it was exactly what I needed, so +1 for a useful answer.