What's the best way to back out multiple changesets in mercurial?

前端 未结 5 759
Happy的楠姐
Happy的楠姐 2021-02-13 01:33

Is the most reliable method to go one-by-one, using the backout command for each of many changesets, or is there a way to create one big reversal changeset to cover a whole bunc

相关标签:
5条回答
  • 2021-02-13 02:05

    There is --collapse option for rebase.

    Helgi's answer can be upgraded into:

    1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5
                              \
                               C' -- B' -- A'
    
    $ hg update --clean C
    $ hg backout --rev C --message "Backed out changeset: C"
    $ hg backout --rev B
    $ hg commit --message "Backed out changeset: B"
    $ hg backout --rev A
    $ hg commit --message "Backed out changeset: A"
    $ hg rebase --collapse --source C' --dest 5
    $ hg commit --message "Backed out C, B, A"
    

    which will result in the following

    1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5 -- C'B'A'
    

    However, backing out in separate branch may result in [logical] conflict in the subsequent merge.

    1 -- A -- 2 -- B -- 3 -- X  --  4 
                         \           \
                          B' -- A' -- M
    

    if X depends on A or B, then M will have conflict (at least logical conflict).

    0 讨论(0)
  • 2021-02-13 02:15

    If you don't want the "backout" changesets in your history, you could also do something else:
    Make a clone of your repository, but only up to the last changeset that you don't want to get rid of.

    See Mercurial: Fix a borked history for an example how to do this.

    If your repository was a local one, that's all you have to do.
    But if the bad changesets were already pushed to a central repository, you'd need server access to delete the repository there and replace it by your clone.
    Plus, if someone else already pulled from the repo with the bad changesets, they need to delete and re-clone (otherwise the bad changesets are in the central repo again as soon as one of the other people pushes again).
    So it depends on the circumstances whether this solution is a good one for you...

    0 讨论(0)
  • 2021-02-13 02:21

    If you have no merges along the way, you can either back out every individual change (in reverse order), or, if there are many of them, do it with one big inverse patch.

    If you have good changesets atop the ones you need to back out, better commit the inverse patch on top of the most recent bad changeset, then rebasing them onto the tip of the branch.

    1 -- 2 -- A -- B -- C -- 3 -- 4
                         \
                          C'B'A'
    
    $ hg up C
    $ hg diff -r C:2 > backout.diff
    $ hg import --no-commit backout.diff
    $ hg ci -m "Backout A, B, C"
    $ hg up 4
    $ hg rebase -s C'B'A -d .
    

    There will be problems if you want to back out merge changesets, see this wiki page for more information.

    In such a case, if possible, consider re-doing the branch and stripping the old lineage. Otherwise, you might have to abandon the branch altogether, salvaging the good changesets via graft or transplant.

    0 讨论(0)
  • 2021-02-13 02:21

    What I came up with is inelegant, but got the job done, despite that the changes I needed to back out were interspersed with other work and had some internal branching. Here's what I did. (Comments and improvements are welcome.)

    Got a list of all of the changesets (which I then used to generate the commands below):

    hg log -r 'keyword(xyz)' --template '{rev}\n'
    

    Generated a patch for each changeset:

    hg diff -p -U 8 --reverse -c 15094 > 15094.rev.patch
    hg diff -p -U 8 --reverse -c 15095 > 15095.rev.patch
    ...
    

    Then, applied each reverse patch. Here the order matters, last-to-first:

    hg import -m "reversing changeset 15302" 15302.rev.patch
    hg import -m "reversing changeset 15292" 15292.rev.patch
    ...
    

    This process was interrupted several times for merges that didn't go through automatically, where I had to manually apply changes to a file from its .rej file and then manually commit, before picking up the imports where it had left off.

    Finally (in another clone... did I mention I did this all in a clone?) I compressed the whole set of reverse changesets into one changeset using hg histedit -o and its fold command.

    Now I've got a single changeset that I should be able to reverse and apply if I decide to put the work back in at a later date (Although if I cross that bridge, I might apply the "forward" patches piecemeal again in order to get better blame/annotate information)

    0 讨论(0)
  • 2021-02-13 02:22

    This is how you can do it with TortoiseHg. Of course you can do the same with the command line.

    Given this history, where you wan't to get rid of changeset A, B and C:

    1 -- 2 -- A -- B -- C -- 3 -- 4
    

    First update to revision 2.

    Then rebase the first of any later revisions you wan't to keep - in this case revision 3.

    Your history now looks like this:

    1 -- 2 -- A -- B -- C
          \
           3 -- 4
    

    Now update to revison 4.

    And finally use "Merge with local" to merge revision C onto revision 4.

    At this point it is crucial that you select the option "Discard all changes from merge target (other) revision".

    The description may not be the most logical, but it means that you merge the old tip C back to the default branch - but without the changesets A, B and C.

    The result is:

    1 -- 2 -- A -- B -- C --
          \            /
           3    --    4
    

    Commit and you're done.

    0 讨论(0)
提交回复
热议问题