Split out a branch into a new repo - mercurial

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.

I think you can do almost everything you stated using hg strip. Something like this:
Clone the original repo to a new repo
hg up null to remove anything from the working directory (probably quicker to work with it like that)
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.
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:
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.
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)
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:
Clone the original to clone1
Use strip to get as much done as you can
Make clone2 from clone1
Run convert task #1
Make clone3 from clone2
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.

Just use described in wiki format of branchmap. In your case it will be easy plain
default-other default
...
renamed-child1 default
renamed-child2 default
...
Addition
Because Convert extension can't stop conversion of condition "before ", only "after " (see --rev option), you must to prepare repository for conversion before convert process (clone into new intermediate repo only subset or original changesets without default, only default-other and descendants?)

Related

Remove branches in Mercurial but keep changes?

I just found out that we're not supposed to create named branches in our local Mercurial repos, because they get carried along and pushed to the upstream repo, where they live forever.
Unfortunately, I already have multiple feature branches in my local repo, which I was all set to merge to default and push up to the main repo. So, what I need to do is keep the work in these branches (somehow) but remove the named branches.
Is there a way to do this, short of a lot of horrible manual revert/cut/paste?
Using TortoiseHg:
(Ensure rebase extension is enabled in File -> Settings -> Extensions)
Update to parent of the first change set of the named branch.
Right click on the first change set of the nambed branch and click rebase.
Ensure "Keep orignal branch names (--keepbranches)" is NOT checked.
Ensure "Rebase entire source branch (-b/--base)" is checked.
Click rebase.
Another approach would be to use the hg convert command (a standard extension) and the branchmap option.
For a private repository or at least one with changesets which have not been published this would work fine, even though like a rebase it would modify history.
An advantage as opposed to using rebase is that it could be used to deal with multiple branches (named or unnamed) all at once. That might be quicker / easier if there was a lot of things to move around.
A secondary plus is that since convert will create a NEW repository there is no risk to the original if something doesn't work as expected. (Of course when using rebase you could make a backup first which I think is a reasonable precaution.)

Overwriting branch with default in Mercurial

Let say I had a feature branch called feature_x that was worked on, then changesets cherry-picked and transplanted to default, then the branch was closed. Not the best flow, but it's Mercurial, so there is no way of changing the history.
Now I'm going to work again on the feature X, and I feel reusing feature_x branch would be least confusing. However, if I reopen that branch and merge default to it, I've got two problems. First merge conflicts, second changes that were modified in that branch, but never merged into default. So what I'd like to have is clean slate, branch feature_x, but with exact copy of what's currently in the default. Is there a cleaner way of doing that, than creating new branch which will shadow the name?
I think your best bet is to start a new branch off of the current tip of default called feature_x2 or feature_y and leave the past in the past.
But here are some other options:
Is the old feature_x branch confined locally to your repo only or was it pushed? If the former, you could hg strip it and start the branch again at the current default.
If the feature_x name is really, really important, you could do the merge default into it using the internal merge tools and force it to reflect the default branch exactly by doing
hg merge -r default --tool internal:other
Or you could just commit a (file system) copy of default on top of the tip of branch_x. Then you could continue on that branch along your merry way.
I don't know if 2. or 3. will cause strange merge issues down the road. I would test to see if the merge back over to default (or another graft?) could cause issues later.

Mercurial: abandoning multiple, contiguous commits, on the `default` branch

Requirement
I'd like to abandon a line of development on the default branch, winding back to a revision from about 15 change sets back, and have default proceed from there.
My setup
This is a solo development project with one other guy testing infrequently. I push (frequently) to bitbucket for backups and sharing with the tester. Several of the changes I want to abandon are pushed to BitBucket.
Options
Any of these would be fineā€¦
The abandoned change sets to continue to exist in the repo. It would be nice if they could live on their own branch abandoned-experiment-1, say, that I can close and ignore, but this would need them to move on to a retrospectively created branch (which seems like it would be an awesome feature?).
Some kind of merge to happen where I add a new revision to default that is the rollback to the revision I want to continue from.
The change sets to be destroyed, but I suspect there's no way to achieve that without replacing the BitBucket repo and my tester's repo, which I'm not keen on.
I'm not too sure how to evaluate which options are possible, which is best, or whether there are other, better options. I'm also not sure how to actually proceed with the repo update!
Thank you.
You do have several options (Note that I'm assuming that you are dispensing with all changes in the 15 or so revisions and not trying to keep small bits of them):
Easiest is kinda #2: You can close anonymous branches just like named branches; Tag the tip first with abandoned-development if you wish; hg update to the point you wish to continue from; and continue to code as normal. (You may need to create the new head for new development before you can close the old one. I haven't tested it yet.)
Regarding #3: Based on my cursory read, it does appear that bitbucket has a strip command. If you (both locally and on bitbucket) and your tester strip the offending changesets, you can carry on your merry way and pretend like they never existed.
Achieving #1: If you are definitely set on getting them to a named branch, you could strip them at the remote repos and then hg rebase them onto a new branch locally and then close that branch.
Personally, I try not to mess with history when I can avoid it, so I'd go with the easiest.
Mercurial now has (yet experimental) support for changeset evolution. That is you are able to abandon or rebase already pushed changesets. Internally this works by hiding obsolete changesets (i.e. practically nothing is stripped, only new replacement revisions are added to the history, that is why it works across multiple clones).
To extend #Edward's suggestions, you could also update to the last good revision, continue to commit from there and then merge in the head of the bad changesets using a null-merge:
hg up <good-revision>
... work ... commit ...
hg merge <head-of-bad-revisions>
hg revert --all -r .
hg commit -m 'null-merge of abandoned changesets'
This may be what you thought of as option 2.

What to do instead of squashing commits in Mercurial

I've got my IDE set to commit locally every time I save anything. I'd ideally like to keep an uncensored record of my idiot fumblings for the rare occasions they may be useful. But most of the time it makes my history way to detailed.
I'd like to know a good strategy to keep that history but be able to ignore it most of the time. My IDE is running my own script every time I save, so I have control over that.
I'm pretty new to Mercurial, so a basic answer might be all I need here. But what are all the steps I should do when committing, merging, and reporting to be able to mostly ignore these automatic commits, but without actually squashing them? Or am I better off giving up and just squashing?
Related question about how to squash with highly rated comment suggesting it might be better to keep that history
Edit - My point here is that if Mercurial wants to keep all your history (which I agree with), it should let you filter that history to avoid seeing the stuff you might be tempted to squash. I would prefer not to squash, I'm just asking for help in a strategy to (in regular usage, though not quite always) make it look as much as possible like I did squash my history.
You want to keep a detailed history in your repo, but you want to have (and be able to export) an idealized history that only contains "reasonable" revsets, right? I can sympathize.
Solution 1: Use tags to mark interesting points in the history, and learn to ignore all the messy bits between them.
Solution 2: Use two branches and merge. Do your development in branch default, and keep a parallel branch release. (You could call it clean, but in effect you are managing releases). Whenever default is in a stable state that you want to checkpoint, switch to branch release and merge into it the current state of default-- in batches, if you wish. If you never commit anything directly to release, there will never be a merge conflict.
(original branch) --o--o--o--o--o--o--o (default)
\ \ \
r ... ... --r--------r (release)
Result: You can update to any revision of release and expect a functioning state. You can run hg log -r release and you will only see the chosen checkpoints. You can examine the full log to see how everything happened. Drawbacks: Because the release branch depends on default, you can't push it to another repo without bringing default with it. Also hg glog -r release will look weird because of the repeated merges.
Solution 3: Use named branches as above, but use the rebase extension instead of merging. It has an option to copy, rather than move outright, the rebased changesets; and it has an option --collapse that will convert a set of revisions into a single one. Whenever you have a set of revisions r1:tip you want to finalize, copy them from default to release as follows:
hg rebase --source r1 --dest release --keep --collapse
This pushes ONE revision at the head of release that is equivalent to the entire changeset from r1 to the head of default. The --keep option makes it a copy, not a destructive rewrite. The advantage is that the release branch looks just as you wanted: nice and clean, and you can push it without dragging the default branch with it. The disadvantage is that you cannot relate its stages to the revisions in default, so I'd recommend method 2 unless you really have to hide the intermediate revisions. (Also: it's not as easy to squash your history in multiple batches, since rebase will move/copy all descendants of the "source" revision.)
All of these require you to do some extra work. This is inevitable, since mercurial has no way of knowing which revsets you'd like to squash.
it should let you filter that history to avoid seeing the stuff you might be tempted to squash
Mercurial has the tools for this. If you just don't want see (in hg log, I suppose) - filter these changesets with revsets:
hg log -r "not desc('autosave')"
Or if you use TortoiseHg, just go View -> Filter Toolbar, and type in "not desc('autosave')" in the toolbar. Voila, your autosave entries are hidden from the main list.
If you actually do want to keep all the tiny changes from every Ctrl-S in the repo history and only have log show the subset of the important ones, you could always tag the "important" changesets and then alias log to log -r tagged(). Or you could use the same principle with some other revset descriptor, such as including the text 'autosave' in the auto-committed messages and using log -r keyword(autosave), which would show you all non-autosaved commits.
To accomplish your goal, at least as I'd approach it, I'd use the mq extension and auto-commit the patch queue repository on every save. Then when you've finished your "idiot fumblings" you can hg qfinish the patch as a single changeset that can be pushed. You should (as always!) keep the changes centered around a single concept or step (e.g. "fixing the save button"), but this will capture all the little steps it took to get you there.
You'd need to
hg qinit --mq once to initialze the patch queue repo (fyi: stored at \.hg\patches\)
hg qnew fixing-the-save-btn creates a patch
then every time you save in your IDE
hg qrefresh to update the patch
hg commit --mq to make the small changeset in the patch queue repo
and when you are done
hg qfinish fixing-the-save-btn converts the patch into a changeset to be pushed
This keeps your fumblings local to your repo complete with what was changed every time you saved, but only pushes a changeset when it is complete. You could also qpop or qpush to change which item you were working on.
If you were to try the squash method, you'd lose the fumbling history when you squashed the changesets down. Either that or you'd be stuck trying to migrate work to/from the 'real' repository, which, I can tell you from experience, you don't want to do. :)
I would suggest you to use branches. When you start a new feature, you create a new branch. You can commit as many and often as you like within that branch. When you are done, you merge the feature branch into your trunk. In this way, you basically separate the history into two categories: one in fine-grain (history in feature branches), and the other in coarse-grain (history in the trunk). You can easily look at either one of them using the command: hg log --branch <branch-name>.

Managing long lived named branches in Mercurial

So I'm using mercurial for a project of mine, I'm the only developer.
I usually use the default branch for actual developing, I use some short lived branches for new-features, and that's fine: I create them, write the new feature and if it works good enough, I merge that branch in the default branch and never use it again.
But I'd like to write documentation in a different branch, since I don't really want to "pollute" the default branch with docs commits.
After I have written enough documentation for the stuff I have in the default branch I merge the docs branch in the main one. BUT after a while I'd like to use the docs branch again, and I have to pull the changes from the main one, or create another new branch.
What's the best workflow to deal with this? Is my approach entirely wrong?
Placing documents in source control is a little bit strange. If the documents are binary (.doc/.docx/.xlsx), Hg will not be able to merge them. If you're storing .html, .xml, or some plain text format then it will do a slightly better job. There are a few open source systems that will allow you to use Hg and provide separate document management (Redmine, for one)
Assuming you've just merged docs into default you can continue using the docs branch by doing this:
> hg update docs # update to the docs branch
> hg merge default # merge default into docs branch
(do some work)
> hg commit -m "adding new things to docs branch"
(merge into default when ready)
By merging default into docs, you're making sure that docs has all changes that existed on default. Performing a subsequent commit on docs will effectively allow you to continue working on that branch. Another way to say this is that merging is directional in Hg - if you want docs to be up to date with default, you've got to perform that merge explicitly.
To start using the docs branch again from a child of your merge with the original docs branch, simply change your working copy to the branch and commit to it.
If you have changes on the docs branch then you need to merge before you can commit.