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.
Related
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
I guess I must have misunderstood the author of this post when he said:
hg pull -r X -f repo # ... will give me all changesets up to X
Because when I did that (in a freshly-created repository), I only got those changesets that were ancestors of the branch that revision X is on.
What I wanted was all the changesets that had been committed to the remote repo up to and including X, chronologically. In other words, in addition to the branch that X is on (and its ancestors), I also wanted all other branches (including closed branches) committed before X that hadn't been merged to X's branch.
How would I express the command to do that?
BTW, in this particular repo, there are closed branches that have names that are identical to currently open/active branches, so if the solution involves enumerating all the branch names (which would be tedious, but do-able), it would still need to get the closed occurrences of such branches as well as the open ones.
(For completeness I suppose I should also say that I ran the command from the command-line of TortoiseHG 2.7 on Windows, in case the behavior of hg pull that I've described above isn't what I should have expected.)
You can't do that on pull in a single command. "Chronologically" means a lot less than you think it might. Anyone can do a commit with any timestamp they want, so the dates aren't good selectors. If you mean "with an earlier revision number" those too can change from repo to repo, so pulling all revisions with a revision number lower than N could give different results for different invocations.
If you want to try the revision-number-based version anyway, you'd probably have your best luck pulling everything to a trash repo locally and then pushing only what you want to a new local repository:
hg clone http://remotehost/path local-clone # clones everything
hg init another-local-clone
hg push --repository local-clone --rev '0:X' another-local-clone
after that another-local-clone will have all the changesets whose revision numbers is X or lower in local-clone, which is (but isn't guaranteed to be) the same as the remote clone
If that seems awkward it's because "committed before" isn't a terribly useful concept in DVCS land -- it assumes a linearity that neither git not Mercurial consider important.
I faced today my first mercurial problem. I was in my repo, I modified a file and I did a
hg commit
hg pull
followed by
hg update
hg rollback
to repair what I've done, (but actually I didn't push anything)
The problem is that when I did the pull (that I should do before the commit, the head changed and so hg heads looks like :
- Modif from yesterday
- My modif
- Modif from last week
and now I see that someone also did another modification (via the http interface). What should I do to repair my local repo, (if possible modifying my summary) and push it after the 2 others modifications.
Thanks a lot. Quiet confusing, was easier on my "one-man" repo..
Your local repo doesn't need "repairing". This is a very standard case that you will see often if you use Mercurial a lot.
The issue is multiple heads.
You can either merge your heads, assuming your working directory is your version and there is only the other head:
hg merge
This will result in a merge changeset (same as if you were merging across branches).
Or you can enable the rebase extension to re-base your version onto the tip of the branch (the other head):
hg rebase --source<YourVersionNumber> --dest<TipVersionNumber>
This will not result in a merge changeset, and will simply transplant your changes on top of the changeset you specify as if they were born of that changeset all along (hence rebase or "new"basing).
Multiple heads is a funny sort of inner-branch branching... you can continue checking stuff in against your own head and change between heads using hg update. We block multiple heads per branch on our server, so our push would fail. I'd advise keeping multiple heads local as they are less clear-cut than branches.
I tend to work with Mercurial in one of two ways:
If the work is large in scale I will branch it off and follow Continuous Integration practices (constantly merging the main branch into my own etc). I then re-merge back into main when I am happy with the end result.
If the work is small in scale I will simply work against main branch and "merge" heads every so often. I say "merge" as I usually use rebase. Re-basing works great if the changes are simple and conflicts are unlikely.
My hard and fast rule: if I can't use rebase I put it on a branch born of main.
In my repo, I have the revisions 1 to 10. I've pushed up to 5 (so the next hg push would publish revisions 6-10).
But I have to interrupt my work now and the result isn't 100% complete. So I'd like to move the revisions 6-10 into a new "experimental" branch to allow someone else to complete the work without disrupting the sources for everyone.
How can I add a branch to a non-tip revision (in my case: Starting with revision 6)? Or should I use a completely different approach?
You cannot apply a branch name after the fact without modifying your history.
The most simple approach is to ask the other users to use revision 5 as the parent for any changes they create. For example, the other users would:
hg clone <your repo> or even hg clone --rev 5
hg update -r 5
work, work, work
hg commit
When they commit a change, it will create a second head on the default branch, but that should not create any problems. You will simply need to merge the two heads together once your experimental changes are complete.
That being said, moving your changesets onto a branch can be accomplished using Mercurial Queues (MQ). The following sequence shows how it be done:
hg qinit (Create a new patch queue)
hg qimport --rev 6:10 (import r6-10 into a new patch queue)
hg qpop -a (remove all patches from your working copy)
hg branch <branch name> (create your new experimental branch)
hg qpush -a (apply all the patches to your branch)
hg qfinish -a (convert all patches to permanent changesets)
Tim already has good suggestions. Additionally you could push your experimental changes into a distinct experimental clone on your central server (I guess you use one). This clone could also be used by other developers to push their not-yet-finished work in order to let others review or continue it. It is also clear that this clone's code is not ready to be used. Once some task is finished, the corresponding changesets can be pushed to the stable repository.
Actually named branches are a good idea for your case, but the fact that their names are burned into history mostly is more a problem than a feature. IMHO Git's branch names are more practically. However, to some extend you could also handle your case with bookmarks, which are pushable since Mercurial 1.7 (not sure here). That is you bookmark revision 5 with something like stable (or whatever you agree on in your team) and revision 10 gets bookmarked with something like Aarons-not-finished-work. The other developers would then just pull stable, except your colleague who is supposed to continue your work, who would pull the other bookmark. However, personally I did not use a such workflow yet, so I cannot say if it performs well in practice.
We converted everything to Mercurial coming from CVS and so far so good. An issue we encountered today though is this situation.
Before the move to Mercurial I had a few pending changes from a while back, features that were started and later postponed for various reason. Odds are that someone else will finish those features months from now picking up from where I left off.
After cloning the new Mercurial repository I created separate branches to isolate those features.
It left me with something like this (made up rev. number)
hg update default
hg branch feature1
hg commit -m "Description of what I was doing in feature1"
hg update default
hg branch feature2
hg commit -m "Description of what I was doing feature2" (my tip is now here)
hg update default
hg push -f (to force the creation of my new branches, w/o affecting default, I haven't merged them)
During this the team have been working and pushing to our central repository so the default branch is say rev 40 (tip)
Now my push -f worked fine but positioned (tip) to my latest change -> 50:feature3 (tip). I was expecting the tip to stay at default on the central repository and just have my branches in there for someone to pick them up whenever. The graph also looks pretty funny when I use hgwebdir so I am pretty sure that's the wrong approach.
How would I do this? Should I close the branch first? Is tip actually important or just meta-data.
tip is always the most recent changeset added to the repository. From hg help revs:
The reserved name "tip" is a special tag that always identifies the most recent revision.
So long as the head of the default branch is what you expect, you'll be OK. No need to close the branch (but it's better to use hg push --new-branch if your version of Mercurial is new enough to support it).
tip is just an automatically-applied label referring to (I think) the most recent commit. Not a big deal; it's just there for convenience.
The tip label is pure meta-data and always points to the changeset with the highest revision number -- there is no more logic than that.
However, the fact that tip now points to a changeset on the feature branch wont cause you any trouble. When people make a clone, they will automatically be updated to the tip-most changeset on the default branch. So they can begin working right away after a clone. Furthermore, people who already have a clone will stay on their named branch when they run hg update. Here hg update takes you to the tip-most changeset on that named branch, e.g., on default if that is where you started.
People may think that hg update tip is the same as hg update, but that is only when there are no named branches at play. With named branches, giving an explicit revision name such as tip can changeset your named branch -- a plain hg update cannot.