tags:

views:

18913

answers:

6

I would like to remove selected commits from a linear commit tree, so that the commits 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 commits. 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?

Some notes that might be helpful:
This is a fairly new project so has no branches as of now, hence no merges as well.

Side note: It's a personal project, so no, I'm not trying to destroy any evidence :)

+43  A: 

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
J.F. Sebastian
Thanks for the quick reply. So do I checkout A and do a rebase, something like `git rebase -i D [A]`?
xk0der
Wow! I've recently started using git and I get surprised on a regular basis now by its power. :). Thank you so much!
xk0der
Thanks for the latest edit, I already got it done though :) git-rebase is so cool.
xk0der
+7  A: 
# 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
Charles Bailey
+3  A: 

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
The OP wants to combine B,C,D commits, not to delete their changes.
J.F. Sebastian
+1  A: 

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
Head
OP wanted to remove the commits but retain the changes introduced by the commits (i.e. squash them) your solution would effectively revert them. This is also quite a severe "necro-post".
Charles Bailey
There's absolutely nothing wrong with necroing here, since this is not a forum, but rather a compendium of useful information. The difference is that people are more likely to arrive here from Google than from some listing of recently-commented-on topics.
Xiong Chiamiov
I'd have to agree with Xiong here--necro'ing should be ok if it's useful.
rogerdpack
+5  A: 

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)

pydave
+1  A: 

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 HEAD

rado
works like a charm, thanks
phil pirozhkov