tags:

views:

953

answers:

3

I have a commit in a remote+local branch and I want to throw that commit out of the history and put some of them into an own branch.

Basically, right now I have:

           D---E---F---G master

And I want:

             E---G topic
            /
           D master

That should be both in my local and in the (there is only one, called origin) remote repository.

Which is the cleanest way to get that?

Also, there are also other people who have cloned that repo and who have checked out the master branch. If I would do such a change in the remote repo, would 'git pull' work for them to get also to the same state?

+2  A: 

You can rewrite your history if you so desire, but it is a bad idea if anyone else has copies of the history. In this case, you would probably use interactive rebase: git rebase -i master topic. This will give you a list of commits from master to topic, with hints about how to play with them. You'd just need to remove the line containing the commit you want to remove.

That said, I must emphasize that it is irresponsible to do this if anyone else has this history. You would have to force-push it to your central repo, and everyone else would have to fix their repositories to match, can be relatively simple or complex depending on circumstances.

There's a nice section called "recovering from upstream rebase" in the git-rebase man page discussing how to deal with this, if you really decide to.

Edit:

For simple history, a common scenario would be, after forcing a non-fastforward push to the central repo (push -f), other developers:

  • back up their old master: git branch -m master master_old
  • get updates and recreate master from origin: git remote update origin; git branch master origin/master
  • rebase all topic branches onto new master: git rebase --onto master master_old topic

If they have work in their master branch which is not in origin yet, they'll have to get fancier, rebasing this work and all topic branches onto the new position of master... this should give you an idea why it's so awful to rewrite history that other people have. Really, once something's passed into the public repository, you should regard it as hard-and-fast recorded history, not a work in progress.

Jefromi
Ok, thanks a lot for those information. So that isn't really a solution then because I don't want to do such force-push.What would be the cleanest solution to get a similar result?The one I would think now is to revert E,F,G in master, then create a new branch from that state and cherry-pick E and G. Is that what one would naturally do?
Albert
There is *no way* to remove a commit from the history without causing these kinds of issues. No matter what you do, you are going to end up pointing your master ref to a commit which doesn't have master's previous position in its history.
Jefromi
This is largely addressed in my answer to your new version of this question, but to answer your comment: it does not matter how you get into a state in which you have rewritten master's history. You will still have rewritten it. If you do all these things *locally*, it's fine. That's called cleanup. If you do them to published work, you're messing things up, creating a public repo no one can trust, etc.
Jefromi
With reverting E,F,G, I meant to use git-revert. So history would look a bit ugly but I would not have to deal with those problems, because I really *dont* want to change already pushed history.
Albert
Ah, okay. Sorry, I misunderstood and thought you were talking about pointing master to the cherry-picked set. If you want to create that new branch, you could still use the same rebase method - just do it on master-new, not master.
Jefromi
+3  A: 

If you've published then you are right that you don't want to re-write the history of master. What you want is to publish a commit to master that brings it back to the state that it was at D while retaining its current history so that other users can merge or rebase their work easily.

If you are planning at some point in the future to merge topic into master then what you probably also want to do is make a new common base between master and topic, so that when you do subsequently merge topic, you don't lose the commits that were reverted in master. The easiest way to do this is by making a 'redo' commit on top of the 'undo' commit that resets master back to its original state and basing the new topic branch on top of that.

# checkout master branch (currently at G)
git checkout master

# Reset the index to how we want master to look like
git reset D

# Move the branch pointer back to where it should be, leaving the index
# looking like D
git reset --soft HEAD@{1}

# Make a commit (D') for the head of the master branch
git commit -m "Temporarily revert E, F and G"

# Create the new topic branch based on master.
# We're going to make it on top of master and the 'undo'
# commit to ensure that subsequent merges of master->topic
# or topic->master don't merge in the undo.
git checkout -b topic

# Revert the undo commit, making a redo commit (G').
git revert HEAD

As an alternative you could have made commits E', F' and G' redoing each part separately but as E, F and G are already in your published history it's probably more understandable if you just reference the 'undo' commit and say that that commit is being undone. This is what git revert does, anyway.

Essentially what you know have is this.

D -- E -- F -- G -- D'      <-- master
                     \
                      \
                        G'  <-- topic

The important things are that you have not rewritten history and topic is based on master so merges won't accidentally apply any 'undo' commits. You can now safely push both master and topic to your remote repository.

Charles Bailey
Thanks, this is exactly what I did now (also what I suggested in some of the comments to Jefromi). History is a bit ugly now because of this undo-commit but I cannot do anything about it.
Albert
2 weeks git user here. Wouldn't it be easier to first branch G to G', and then `git revert` G to D, resulting in D'? That way you don't have to undo/redo anything, which feels a lot safer to me. (I've already unrecoverably lost edits due to messing with `git reset`.)
bart
A: 

I find git stash is pretty helpful

Just stash it away and never look at it again.

twig