Duplicate a branch in Mercurial? - mercurial

How can I duplicate a branch in Mercurial? I need the new branch to be against head (as the first one is).
The GIT equivalent (if I was in branch-a) would be:
git checkout -b branch-b

A Mercurial branch is a named entity that consists of all the commits contained within the branch. So in order to duplicate some existing branch, you must also duplicate all of its commits to new commits that are in the new branch. We then get into metaphysics arguments about commit identity. It's probably not a good idea to go here at all, but if you do want to go here, use hg graft to copy all the desired commits into the new branch.
A Git branch consists of a name containing a raw commit hash ID. So duplicating a Git branch under a new name is trivial. Note that the set of branches that contain any given commit changes dynamically over time: a branch that was only on feature/tall may now only be on master, even though that commit is still that commit, even via most of these metaphysical arguments. (Only the "no identity over time" argument lets us claim that this is not the same commit.)
Another way to put it is that Mercurial's branches actually mean something, but Git's don't. If you need true branches, you can't use Git in the first place. Don't try to import Git's bizzareness into Mercurial: you'll just make your own life miserable.
Meanwhile, though, Mercurial contains a DAG just like Git. If you use Mercurial bookmarks, those work like Git branches. It's probably wiser, then, to just use bookmarks and be done with it.
If all else fails, see hg graft.

the new branch to be against head
What is this (in usual business-term, not Git-lingua)? While in common (and in details) #torek is totally right, he forgot to write exact command-set, something like
hg up <rev-id>
hg branch <new-branch-name>
hg graft -r "branch(old-branch-name)" --log

Related

Sort hg branches by modification time

When cleaning up branches in git, I have been using something like this to differentiate between old/inactive and new/active branches.
How can I sort all branches by their modification time in Mercurial?
The concept of a branch is just different enough in Mercurial vs Git to make this a bit tricky. Depending on what you really want, the "trickiness" may be that it becomes completely trivial!
Start with hg heads, which shows all heads (see hg help heads for the definition of a head). Each head is a commit, and therefore:
has an ID;
is in one particular branch;1 and
has a date stamp.
It also has a local revision number within your repository, which won't necessarily match the revision number in some clone that has the same commit. That is:
$ hg heads
changeset: 5:5f5df3fc4f1c
Changeset 5f5df3fc4f1c in some clone might be 4:5f5df3fc4f1c or 100:5f5df3fc4f1c or some such. The number in front of the colon is the local revision number.
Now, the dates on commits are up to the machine that creates the commit, so they can be quite wildly wrong. The local revision numbers are assigned sequentially as you add commits to your Mercurial repository, though—so if a local revision number is higher, that means that you introduced the commit later. (Caveat: this could be "a lot later than actually made" if you just now brought new commits in from an old, inactive, but never-before-incorporated clone.)
In that sense, then, the output of hg heads, which is shown by default in reverse local-number order, is already in the order you probably want (or the reverse of it). Things printed later are "less active". So there's probably no work to do here other than to read through the output of hg heads (and check whether there are multiple heads within some branch).
1This differs from Git in that in Git, a tip commit is the commit to which some refs/heads/ name points. That tip defines the branch, but that commit can be in multiple branches! The tip is necessarily a head in the Mercurial sense,2 but there can only be one such head, because it's the branch's name that locates and thereby defines the head / tip commit, and each name identifies only one commit.
2Unless, that is, you mean hg heads --topo, in which case a Git commit identified by refs/heads/name might not be a head after all:
...--o--* <-- refs/heads/midpoint
\
o--● <-- refs/heads/tip
The commit identified by refs/heads/tip is a topological head while that identified by refs/heads/midpoint is not. In Git the midpoint commit is on both branches.
If you do want different sortings, it's harder: instead of hg heads, you must use hg log and use a revision specifier. Note that -r "head() and not closed()" produces the same output as hg heads, so you can start with that. The output from hg log is sorted in whatever order you choose, but defaults to the same local-numeric-revision sorting. See hg help revisions for more.

Disassociate a file on a feature branch from parent in mercurial?

Is there a way to disassociate a file on a feature branch from its parent branch, in that changes to the file on the parent branch will no longer merge into the feature branch?
ie: app/index.html [develop] changes should no longer merge into app/index.html [feature/redesign] when merging develop into feature/redesign ?
You should ask yourself why you want to do this, it sounds backwards. Anyway, the following should work: perform the merge as usual, don't care about what happens to app/index.html (that is, in case of conflict just accept something, don't think about it). Then, before doing the merge commit, revert that file to its previous revision on redesign branch. To make an example:
(on branch redesign)
hg merge develop
hg revert -r redesign app/index.html
Now hg status will still show app/index.html as modified, but if you look at its contents, they are the same as before the merge. Do the merge commit as usual:
hg commit -m 'Merge develop into redesign'
Note: you must do this each time you do a merge, there is no "dissociate" capability.
Just delete and re-create it on the branch.

mercurial hg up -C created a branch with the same name as current, how to remove it or merge it

I had a change in my branch and after unseccessfull merge, I tried to revert that unsuccessefull merge with hg up -c but it created a new branch instead. I can merge it into current or discard it or what to do with it?
EDIT:
actually I did the following, I had a branch and committed changes there there. then I wanted to push my changes to server, so I pulled changes, and tried to merge with them, but there were a conflict I couldn't resolve myself and I thought: I'll revert all changes back and merge again - so used hg up -C which I thought, will revert everything I changed during my unfinished merge. But what actually happened, another branch with the same name was created, containing only that changes I committed previously and with the same name as a branch I was working in and I was switched to the branch where I was working, which didn't have my changes. So two questions here: what actually happened and why another branch with the same name was created?
Having multiple heads on the same branch, which I think is what you're saying with "a branch with the same name as current" is a normal situation and, yes, you can use hg merge to consolidate them into one head. Use the hg heads command to find the hashes of the two heads of branch X. Then:
hg update REVISION_ID_OF_ONE_HEAD # changes your working directory to match one of the heads
hg merge REVISION_ID_OF_THE_OTHER_HEAD # merges that head's changes in
hg commit # create a new changeset that is the child of both those heads thus reducing the head count by one
Also #ringding is correct that hg update never creates branches. You either already had them and didn't know or received another head when you pulled.

Mercurial push problem

I've just got a problem with hg push command. What I did - Firstly I created 2 branches hot-fix-1 and hot-fix-2 made some changes in each branche, merged it back to default and closed those branches with the command:
hg commit --close-branch
If I start hg branches I have the following output:
default 29:e62a2c57b17c
hg branches -c gives me:
default 29:e62a2c57b17c
hot-fix-2 27:42f7bf715392 (closed)
hot-fix-1 26:dd98f50934b0 (closed)
Thus hot-fix-* branches seems to be closed. However if I try to push the changes I have the next error message:
pushing to /Users/user1/projects/mercurial/mytag
searching for changes
abort: push creates new remote branches: hot-fix-1, hot-fix-2!
(use 'hg push --new-branch' to create new remote branches)
and it does not matter which command I use hg push -b . or hg push -b default
So the question is how I can push those changes to repository without creating new branches.
P.S I used to work with git and was hoping that similar branching model can be used in Mercurial. Thanks
First, as many others have pointed out, using a named branch for short lived work is not a recommended practice. Named branches are predominantly for long lived features, or for release management.
Given that you are in this situation, there are a few options available. All of them involve modifying history (as you're obviously trying to change something you've done).
One is to just push the branches as is, learn from the experience, and move on. If the rest of the team is fine with this, then it's a case of adding --new-branch to your push command.
If the rest of the team, or you, really want the history to be clean, then you'll need to dig deeper.
If you aren't pushing, then definitely make a clone of your current repo. This way you have a copy of the original work to fall back on.
I see 2 main approaches here. Strip off the merges and rebase your branches onto default. This will get rid of the named branches or graft/transplant your changes. Both will be the same end result, but the implementation is slightly different.
If you merely want to use graft, that is now a built-in function starting with HG 2.0. It replaces the transplant plugin, and is much nicer to work with as it uses your usual merge tool if there are conflicts.
To use it, update to the default branch. Then, use the command:
hg graft -D "2085::2093 and not 2091"
the string after -D is an hg revision selection query. In your case, you'd likely only need '{start}::{end}' where start is the changeset at the start of the branch, and end is the end changeset of the branch (ignoring the merge).
If you did several merges, you'd have to pick and choose the changesets more precisely.
The other option is to strip the final merges, and use the rebase command that is part of the mq plugin.
You'll have to strip your merge changesets to get rid of them, and then update to the tip of the branch you want to keep. Select the start of the first named branch, and do a rebase. This will change the parentage of the branch (if you're familiar with Git, then this is very much like it's rebase).
Then repeat for the second branch. You should now have one long branch with the name default.
Just do the:
hg push --new-branch
It will send over those branches, but they'll be closed on the receiving end too, so no one should be bothered.
See my comment on the question for why Named Branches are best saved for long-lived entities like 'stable' and anonymous branches, bookmarks, or clones are more suitable for short lived things like hot-fixes and new features.
Your hot-fix changes were made on branches. Regardless of whether the branch is active or closed, it does exist.
To push the changes to the server (without rewriting history), you must use the --new-branch option (e.g. hg push --new-branch`).
Since you merged the branches into default, there will still only be one head (as you have already seen in your local repo).
If you really can't live with pushing the branches to the server, then you must rewrite your local history as suggested in Mikezx6r's answer.
In addition to the methods he mentioned, you can also import the changesets into a patch queue and apply them to the tip of your default.

Retroactive named branching in Mercurial

Is there a way to associate a bunch of Mercurial changesets with a named branch after they have been committed, i.e. retroactively ?
I just wanted to do this, and here’s the solution I found.
A year has passed since the question was originally asked, this might explain why I can now find a solution.
It has the disadvantage that you create an extra revision in the process, but this wasn’t too bad for me.
First, you go back to where you want to create the branch.
In my case, I actually wanted to start a new root (because I wasn’t very sensible when I started the repository, but anyways), so I’m updating to null. You probably want to start somewhere else, it depends on your situation.
$ hg update null
Then, create the branch.
$ hg branch blah
$ hg commit -m "Created blah branch."
Then, we rebase all the commits we made onto our new branch:
$ hg rebase -s SOURCE -d DEST
The SOURCE here should be the first commit you made in the commits you want to create the branch from, and the DEST should be the commit where the branch was created (the one we committed above).
No, branch names are part of the changeset (it's really like a label you add to the commit), it means the changeset hash depens on the branch name.
So the only way to change it retroactively is by rewriting history (which doesn't play well if you pushed your changes elsewhere, since you'll have to rewrite every repo which has the changes).
To rewrite history, you could use for example mq.
Not a complete solution but, without re-writing history; you could tag the last change set in the unnamed branch. If you never rebranch from this the tag should surfice. If you do need to rebranch you can move on to a named branch after the tagged changeset.