Search code examples
mercurial

Split out a branch into a new repo


I have a mercurial repo that now effectively contains two repos. At some point in the past, a branch was split off from default (let's call it default-other). Both default and default-other now have a lot of commits and there are no plans to ever merge default-other back to default. Some commits have been grafted from default to default-other.

Now, I want to create a new repo from the default-other branch while retaining the full history. i.e. a repo that contains

  • all commits from default up to the point where the default-other branch was created
  • all commits from default-other
  • all branches that have been branched off from default-other
  • no commits from default after default-other was branched off

Optimally, I would like to use different strategies for different branches. Some branches are release branches which must be kept, whereas others are feature branches which I would like to fold into the new default.

I tried using the convert extension, but I cannot figure out how to correctly set up the branch map.


Solution

  • I think you can do almost everything you stated using hg strip. Something like this:

    1. Clone the original repo to a new repo
    2. hg up null to remove anything from the working directory (probably quicker to work with it like that)
    3. Identify all branches (named or anonymous) and changesets that you want to remove. To be precise, what you want is the changeset ID of each commit and its descendants that should be removed.
    4. hg strip -r 12345678890 for each of those commits. This removes the commit & all its descendants (potentially including merges).

    If needed, you can do this procedure twice, but the second time strip the orthogonal set of changesets (those which pertain to default-other but not to default).

    That said, there are a few reasons you might still want to use hg convert:

    1. There are files in the repo that are not relevant. For instance, if default branch had some old stuff which has nothing to do with default-other. You could use convert to eradicate history of such files.

    2. You want default and default-other to look like they were always the same branch. You can use convert to rename one or both of them. (branchmap)

    3. You want to ensure the new repo(s) cannot be pushed/pulled with the original which could mess up all your well-laid plans. By "converting" the entire repo, IIRC all the changeset IDs will be renewed and the repo will not look like a relative of the original.


    My advice would be to work in stages:

    1. Clone the original to clone1
    2. Use strip to get as much done as you can
    3. Make clone2 from clone1
    4. Run convert task #1
    5. Make clone3 from clone2
    6. Run convert task #2

    etc.

    I recommend not batching many different convert things together - run them in small increments. This may seem like it will take longer, but its much easier to get right this way.

    Ultimately you can remove all the intermediate clones, but through experience I found this was the most productive & safest way to make big structural changes.