Mercurial move changes to a new branch

34,980

Solution 1

As suggested by Mark, the MqExtension is one solution for you problem. IMHO a simpler workflow is to use the rebase extension. Suppose you have a history like this:

@  changeset:   2:81b92083cb1d
|  tag:         tip
|  summary:     my new feature: edit file a
|
o  changeset:   1:8bdc4508ac7b
|  summary:     my new feature: add file b
|
o  changeset:   0:d554afd54164
   summary:     initial

This means, revision 0 is the base on which you started to work on your feature. Now you want to have revisions 1-2 on a named branch, let's say my-feature. Update to revision 0 and create that branch:

$ hg up 0
$ hg branch my-feature
$ hg ci -m "start new branch my-feature"

The history now looks like this:

@  changeset:   3:b5939750b911
|  branch:      my-feature
|  tag:         tip
|  parent:      0:d554afd54164
|  summary:     start new branch my-feature
|
| o  changeset:   2:81b92083cb1d
| |  summary:     my new feature: edit file a
| |
| o  changeset:   1:8bdc4508ac7b
|/   summary:     my new feature: add file b
|
o  changeset:   0:d554afd54164
   summary:     initial

Use the rebase command to move revisions 1-2 onto revision 3:

$ hg rebase -s 1 -d 3

This results in the following graph:

@  changeset:   3:88a90f9bbde7
|  branch:      my-feature
|  tag:         tip
|  summary:     my new feature: edit file a
|
o  changeset:   2:38f5adf2cf4b
|  branch:      my-feature
|  summary:     my new feature: add file b
|
o  changeset:   1:b5939750b911
|  branch:      my-feature
|  summary:     start new branch my-feature
|
o  changeset:   0:d554afd54164
   summary:     initial

That's it .. as mentioned in the comments to Mark's answer, moving around already pushed changesets generally is a bad idea, unless you work in a small team where you are able to communicate and enforce your history manipulation.

Solution 2

You can use the MqExtension. Let's say the changesets to move are revisions 1-3:

hg qimport -r 1:3    # convert revisions to patches
hg qpop -a           # remove all them from history
hg branch new        # start a new branch
hg qpush -a          # push them all back into history
hg qfin -a           # finalize the patches

Solution 3

I prefer the patch solution describe here by Mark Tolonen

What I have:

hg log -G

#default branch
@  changeset:   3:cb292fcdbde1
|
o  changeset:   2:e746dceba503
|
o  changeset:   1:2d50c7ab6b8f
|
o  changeset:   0:c22be856358b

What I want:

  @  changeset:   3:0e85ae268e35
  |  branch:      feature/my_feature
  |
  o  changeset:   2:1450cb9ec349
  |  branch:      feature/my_feature
  |
  o  changeset:   1:7b9836f25f28
  |  branch:      feature/my_feature
  |
 /
|
o  changeset:   0:c22be856358b

mercurials commands:

hg export -o feature.diff 1 2 3
hg update 0
hg branch feature/my_feature
hg import feature.diff

Here is the state of my local repository

@  changeset:   6:0e85ae268e35
|  branch:      feature/my_feature
|
o  changeset:   5:1450cb9ec349
|  branch:      feature/my_feature
|
o  changeset:   4:7b9836f25f28
|  branch:      feature/my_feature
|
| o  changeset:   3:cb292fcdbde1
| |
| o  changeset:   2:e746dceba503
| |
| o  changeset:   1:2d50c7ab6b8f
|/
|
o  changeset:   0:c22be856358b

Now I need to delete the revisions 1 2 and 3 from my default branch. You can do this with strip command from mq's extension. hg strip removes the changeset and all its descendants from the repository.

Enable the extension by adding following lines to your configuration file (.hgrc or Mercurial.ini):

vim ~/.hgrc and add :

[extensions]
mq =

And now strip this repository on revision 1.

hg strip 1

and here we are

@  changeset:   3:0e85ae268e35
|  branch:      feature/my_feature
|
o  changeset:   2:1450cb9ec349
|  branch:      feature/my_feature
|
o  changeset:   1:7b9836f25f28
|  branch:      feature/my_feature
|
o  changeset:   0:c22be856358b

note: changesets are different but revisions are the same

Solution 4

For those inclined to use GUI

  1. Go to Tortoise Hg -> File -> Settings then tick rebase .

enter image description here

  1. Restart tortoise UI

  2. Create new branch where you will be moving changes. Click on current branch name -> choose Open a new named branch -> choose branch name.

enter image description here

  1. If changes you want to move have not been made public (e.g draft) go to 5. (If changes have already been published and you are not a senior dev you should talk to someone senior (get a scapegoat) as you might screw things up big time, I don't take any responsibility :) ).

Go to View -> Show Console (or Ctrl + L) then write in console hg phase -f -d 2 - where 2 is lowest revision you will be moving to new branch.

  1. Go to branch and revision (should be topmost revision if you are moving changes to new branch created in step 3.) Right Mouse -> Update

  2. Go to branch and revsion you will be moving changes from Right Mouse -> Modify History -> Rebase

enter image description here

  1. Click Rebase and pray there are no conflicts, merge if you must.

  2. Push changes, at this point all revisions should still be draft.

  3. Go to topmost revision in branch you were moving changes to Right Mouse -> Change Phase to -> Public.

enter image description here

Hope this saves you some time.

Share:
34,980
Casebash
Author by

Casebash

Bachelor of Science (Adv Maths) with Honors in Computer Science from University of Sydney Programming C/C++/Java/Python/Objective C/C#/Javascript/PHP

Updated on July 09, 2022

Comments

  • Casebash
    Casebash almost 2 years

    I have a number of changes that I committed to my local repository, but have not yet been pushed. Since on a feature is taking longer than expected, I want to swap these changes onto a named branch before I push. How can I do this?

  • Casebash
    Casebash over 13 years
    I want to import 63:64 and 66:68. I am getting revision 65 is not the parent of 64
  • Mark Tolonen
    Mark Tolonen over 13 years
    What do you want to do with 65? Mq can only convert consecutive changesets from a head. It turns normally immutable changesets into mutable patches that can be edited. This changes the hashes (affecting all children), so you can't skip.
  • Casebash
    Casebash over 13 years
    I have a number of changes (including 65) that I made on the main branch and pushed
  • Mark Tolonen
    Mark Tolonen over 13 years
    Don't edit changesets that have been pushed. Mq changes the hashes so they will be effectively new changesets. Only edit history that hasn't been pushed.
  • Matt
    Matt over 13 years
    If you've already pushed 65, then you should most definitely not move 63 and 64, and just settle for moving 66:68 (again, only if you have not pushed them).
  • Mark Tolonen
    Mark Tolonen over 13 years
    Additionally, why push to a central location if your feature isn't done? Push when actually done, or push to a teammate to share your work.
  • Casebash
    Casebash over 13 years
    @Mark, @Matt: Both 65 and 63 branch off from 62 (unnamed branch). 63:64 have not been pushed. 65 and some revisions after 69 have been. I don't understand how this could be a problem. I am currently the sole user of my repo - pushing is a nice way to back up my work.
  • Mark Tolonen
    Mark Tolonen over 13 years
    @Casebash, if you push again after moving some changesets that have already been pushed, the remote repo will have both your old and new changesets, and if you pull you'll get your old changesets back.
  • Casebash
    Casebash over 13 years
    @Mark: True, but that's not what I was doing. 63:64 have not been pushed
  • sschuberth
    sschuberth over 11 years
    IMHO the drawback of this solution is that it introduces a "start new branch my-feature" dummy commit (i.e. one that does not change any files).
  • Oben Sonne
    Oben Sonne over 11 years
    @sschuberth: I think being explicit is a good thing here. If the extra changeset is a problem for you, combine it with the succeeding one (e.g. by using the fold command of the now built-in histedit extension).
  • Amir Rachum
    Amir Rachum over 11 years
    How do you print the log with the ASCII tree?
  • Oben Sonne
    Oben Sonne over 11 years
    @AmirRachum: hg log -G (GraphlogExtension). I've stripped off some lines manually, but it could also have been rendered completely automatically using custom log styles.
  • 56ka
    56ka over 10 years
  • anatoly techtonik
    anatoly techtonik over 9 years
    I'd also add hg qq -c rebranch to avoid messing with other patches that may be in queue.
  • weberc2
    weberc2 over 9 years
    @sschuberth I agree. My workaround is to rebase your non-dummy commits onto the dummy commit's parent with the --keepbranches flag, and then hg strip your dummy commit. This is a lot of work to change a branch name, but sometimes Mercurial is dumb like that.
  • Joshua Duxbury
    Joshua Duxbury about 8 years
    Great job! going to try this, just one question though - why change the phase to public at the end? "Any changesets seen in a remote repository are public" so when you push wouldn't it be set to public anyway?
  • Matas Vaitkevicius
    Matas Vaitkevicius about 8 years
    @JoshLeeDucks When pushing they don't change to public automagically anymore (at least for me they don't).
  • Harald
    Harald over 3 years
    @sschuberth after branching, the explicit commit is not actually needed (as of Mercurial 4.5.3 at least:-)