6

I need to revert a whole branch with a single commit like this:

          [topic]
     o---o---o-------X
    /
   |      [master]
---o---o---o

The commit X must have state like a master~2 (the origin point of topic branch)

My solution is:

git rev-list --reverse master..topic | while read SHA; do
   git revert -n ${SHA}
done
git commit -m "Reverted topic branch"

Is there a better (shorter) solution?

A PURPOSE OF THIS

Imagine I have a kind of almost-git-flow based repo.

              [release-3]
       o---o---o---o
      /         \       [fix-for-rc-3]
     /           o---o---o
    /
   |      [development]
---o---o---o

I have a development branch, upcoming release-3 branch and a so-called hot-fix-feature-for-rc-3 branch. In this branch I'm doing some ugly hack to get my release done but I don't want it in a development at all because more "correct" solution is already landed here but can't be applied to a release-3 branch by some reason. So, I must do the following...

                                    [release-3]
       o---o---o---o--------------------M
      /         \       [fix-for-rc-3] /
     /           o---o---o----------------X
    /                                      \
   |                                        \[development]
---o---o---o---------------------------------D

I must merge fix-for-rc-3 to a release-3 (point M), then do a "revert-all-this-shit" commit (point X) and merge it do development (point D) so this code will never get here, even then a whole release-3 branch is merged to development then release is done.

That's why I need to revert a whole branch...

THE PROBLEM

While the root issue is solved, there are still problem getting fork point of a branch cause' if topic is already merged to release merge-base will fail.

Olegas
  • 10,349
  • 8
  • 51
  • 72
  • 1
    You don't actually need to revert the changes to achieve this. You can merge `fix-for-rc-3` into `development` using the "ours" strategy. Or perhaps better, merge `release-3` into `development` using the "ours" strategy. – John Bartholomew Oct 14 '13 at 18:43
  • @kan will not if all of them created by above mentioned workflow, isn't it? Each commit in `release-3` is a branch merge. All I need to do is to merge them all to `development` (may be with `-s ors`). – Olegas Oct 14 '13 at 19:41
  • @JohnBartholomew As I understand he need merge into `development` the `release-3`, then the `fix-for-rc-3` using `ours`; and then merge `fix-for-rc-3` into `release-3`. – kan Oct 14 '13 at 19:42
  • The `X-D` merge will carry the first three commits on `release-3` into `development`, so don't do that merge `-s ours` unless you want to trash them. – jthill Oct 14 '13 at 23:10

4 Answers4

5

You can create a commit that reverts to the base of the branch like this:

# on "topic" branch
git read-tree $(git merge-base topic master)
git commit -m "Reverted topic branch"
git checkout -- . # update working copy to match the committed tree

In your example use, you want to revert the changes so that you can merge the branch back into master (or development as it's called in your example) without actually incorporating any of the changes from the topic branch. But that can be done without adding a revert commit by using the "ours" merge strategy. From the git-merge documentation:

ours

This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy.

For example:

git checkout master
git merge -s ours topic
John Bartholomew
  • 6,428
  • 1
  • 30
  • 39
  • Not working. Then I do commit, git says "nothing to commit, working directory clean". And I agree with him - my working dir is changed but staging area is clean, so, nothing to commit. – Olegas Oct 14 '13 at 18:03
  • @Olegas: did you give exactly the command above? The `-- .` at the end of the `git checkout` command is important. – John Bartholomew Oct 14 '13 at 18:05
  • @Olegas: `git checkout -- .` should update the index with everything that it checks out. (This kind of checkout "writes through" the index.) However, as noted, this won't remove items, so you might want to `rm -r` everything in the work dir first, and then use `git status` to see if there is anything to delete. – torek Oct 14 '13 at 18:06
  • Yes, I understand what `-- .` means. My test repo where I test the solution contains commits which (every one of them) adds an empty new file. May be this is a problem for this solution... – Olegas Oct 14 '13 at 18:10
  • 1
    @Olegas: I have edited the answer to suggest using `read-tree` instead of `checkout -- .`, which should be more robust in dealing with added files. – John Bartholomew Oct 14 '13 at 18:17
  • @JohnBartholomew `merge-base` is broken if topic is already merged to master. Is there another way of getting a fork point? – Olegas Oct 15 '13 at 08:56
  • @Olegas: There are various answers to that: [Finding a branch point with Git?](http://stackoverflow.com/questions/1527234/finding-a-branch-point-with-git) – John Bartholomew Oct 15 '13 at 10:42
2

You can read the tree information of the commit into the index and then commit the index. That way, your new commit will have the exact same tree state as the old one.

git checkout topic
git read-tree master~2
git commit -m 'revert complete branch'

May I ask why you are doing this? What problem are you trying to solve? What's the purpose of having a branch that is then reverted completely?

knittl
  • 246,190
  • 53
  • 318
  • 364
1

What is about "merge" with "ours" strategy?

$ git checkout master~2
......
HEAD is now at 06a96da... c2
$ git merge -s ours topic 
Merge made by the 'ours' strategy.
kan@altegol:/tmp/g$ git checkout topic 
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  cdaad73 Merge branch 'topic' into HEAD
....
Switched to branch 'topic'
$ git merge cdaad73
Updating 0b980be..cdaad73
Fast-forward

In comparison to another solutions this solution is better as it creates merge commit which explicitly shows exact history which points to the commit to which you are reverting to.

kan
  • 28,279
  • 7
  • 71
  • 101
-1

I don't know, if I get this right, but shouldn't it be simply that?

git checkout topic
git reset --hard master~2
KingCrunch
  • 128,817
  • 21
  • 151
  • 173
  • In a real world (not in a tiny example) i practically don't know how many commits i must rewind – Olegas Oct 14 '13 at 18:27
  • So, I can use merge-base, but I need a new commit, not reset. – Olegas Oct 14 '13 at 18:42
  • @Olegas Of course you can use every other ref you like instead of `master~2`. For example use `git merge-base` to find the common ancestor. The "need a new commit" was clear to me thou. However, I'd drop the whole branch and create it from scratch. – KingCrunch Oct 14 '13 at 19:28