tags:

views:

696

answers:

4

Say I have a git repository that looks like this:

A -> B  -> C -> D -> HEAD

I want the head of the branch to point to A, i.e. I want B, C, D, and HEAD to disappear and I want head to be synonymous with A.

It sounds like I can either try to rebase (doesn't apply, since I've pushed changes in between), or revert. But how do I revert multiple commits? Do I revert one at a time? Is the order important?

ANSWER:

Looks like the correct steps are:

git reset --hard <<commit no>>
git push -f

(assuming that no one else has pulled!)

No idea what you do if people have pulled.

A: 

You can use git reset:

Sets the current head to the specified commit and optionally resets the index and working tree to match.

git reset --hard a

--hard resets the working tree and index as well.

If you want to keep those commits, then you could use git revert to revert the commits in reverse order. This way you will not have the problem of erasing history in your public repository. However, if you actually want to erase those commits from the history then you should use git reset.

Karl Voigtland
That puts my working copy at that revision, but what happens if I commit and push? I get an error about divergence when I try to push.
Bill
The push is complaining since it would not result in a fast forward merge. So to also reset your public repository as well pass '-f' to git push to force it to update.
Karl Voigtland
Will that break anything?
Bill
You will of course the also lose those commits in your public repos. as well. To be safe, before doing reset you should make a temp branch off of HEAD, so that you can still reach those commits after the reset.
Karl Voigtland
The main problem will be if other people have pulled from your public repos. then it will mess things up for them.
Karl Voigtland
Great! That seems to have worked!
Bill
lol, the first accepted answer i’ve seen with negative votes
knittl
+2  A: 
git reset --hard a
git reset --mixed d
git commit

That will act as a revert for all of them at once. Give a good commit message.

Autocracy
If he wants `HEAD` to look like `A` then he probably want the index to match so `git reset --soft D` is probably more appropriate.
Charles Bailey
--soft resetting doesn't move the index, so when he commits, it would look like the commit came directly from a instead of from D. That would make the branch split. --mixed leaves the changes, but moves the index pointer, so D will become the parent commit.
Autocracy
+15  A: 

Expanding what I wrote in a comment

The general rule is that you should not rewrite (change) history that you have published, because somebody might have based their work on it. If you rewrite (change) history, you would make problems with merging their changes and with updating for them.

So the solution is to create a new commit which reverts changes that you want to get rid of. You can do this using git revert command.

You have the following situation:

A <-- B  <-- C <-- D                                               <-- master <-- HEAD

(arrows here refers to the direction of the pointer: the "parent" reference in the case of commits, the top commit in the case of branch head (branch ref), and the name of branch in the case of HEAD reference).

What you ned to create is the following:

A <-- B  <-- C <-- D <-- [(BCD)^-1]                   <-- master <-- HEAD

where "[(BCD)^-1]" means the commit that reverts changes in commits B, C, D. Mathematics tells us that (BCD)^-1 = D^1 C^-1 B^-1, so you can get the required situation using the following commands:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --edit B


Alternate solution would be to checkout contents of commit A, and commit this state:

$ git checkout -f A -- .
$ git commit -a

Then you would have the following situation:

A <-- B  <-- C <-- D <-- A'                       <-- master <-- HEAD

The commit A' has the same contents as commit A, but is a different commit (commit message, parents, commit date).

The solution by Autocracy, modified by Charles Bailey is the same solution, only steps are different:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit -a
Jakub Narębski
Excellent answer!
Jeet
+1  A: 

The best resource I've been pointed so far: http://learn.github.com/p/undoing.html

Fabiano PS