Can I clone part of a Mercurial repository? - mercurial

Is it possible to clone part of a Mercurial repository? Let's say the repository is quite large, or contains multiple projects, or multiple branches. Can I clone only part of the repository?
E.g. in Subversion, you might have trunk and branches. If I only want to get trunk (or one of the branches) I can just request [project]/trunk. If I clone the hg repo I'll get trunk and all of the branches. This might be a lot of information I don't want. Can I avoid getting this?
Alternatively, if I want to have multiple projects in one hg repo, how should I do this? I.e. so that I might just get one of the projects and ignore the others.

Yes you can. I'm sure you've moved on, but for the sake of those who will wander here later, I followed the docs at ConvertExtension, and wrote a simple batch script:
#echo off
echo Converting %1
REM Create the file map
echo include %1 > ~myfilemap
echo rename %1 . >> ~myfilemap
REM Run the convert process
hg convert --filemap ~myfilemap .\ ..\%1
REM Delete the file map
del ~myfilemap
cd ..\%1
REM update the new repo--to create the files
hg update
Name it something like split.cmd, and put it in the directory for the repo you want to split. Say for example you have C:\repos\ReallyBigProject, and a subfolder is C:\repos\ReallyBigProject\small-project. At the command prompt, run:
cd\repos\ReallyBigProject
split.cmd small-project
This will create C:\repos\small-project with a slice of the relevant history of revisions from the larger project.
The convert is not enabled by default. You'll need to make sure the following lines exist in your .hg\hgrc file (c:\repos\ReallyBigProject\.hg\hgrc in my example):
[extensions]
hgext.convert=

#Nick
"E.g. in Subversion, you might have trunk and branches. If I only want to get trunk (or one of the branches) I can just request [project]/trunk. If I clone the hg repo I'll get trunk and all of the branches. This might be a lot of information I don't want. Can I avoid getting this?"
Absolutely. Just use hg clone -r <branch> and get only the branch you want. If you have lots of branches, you need a -r <branch> for each one. <branch> doesn't have to be a named branch: you can simply have multiple unnamed heads (or named heads using bookmark, though those still aren't perfect, because currently they don't show up with push/pull/clone).
Keep in mind that in DVCSes, Mercurial among them, branches are often short-lived and merged back into each other frequently. If you pull a branch you will still get the common history it has with any other branches.

To my knowledge, that's not possible. But compared to Subversrion, cloning the whole repos may not be slower than just a branch from SVN.
Quoting from UnderstandingMercurial:
Many SVN/CVS users expect to host
related projects together in one
repository. This is really not what hg
was made for, so you should try a
different way of working. This
especially means, that you cannot
check out only one directory of a
repository.
If you absolutely need to host
multiple projects in a kind of
meta-repository though, you could try
the Subrepositories feature that was
introduced with Mercurial 1.3 or the
older ForestExtension.

#Nick said:
"This is a pretty big omission since a lot hosting sites only offer one repo. With svn I can effectively have as many repos as I want by only taking one branch from the main one. The subrepos sound like a hack."
Subrepos (aka submodules) are not as ideal as "narrow clones" its true. But at least for having many distinct projects in one hosting site's repository, you can have multiple code-bases in one repository. This won't allow you to slice up different sections of one repository / sub-directories of a project , but it will let you manage multiple projects. What you do is have lots of named branches each rooted at the empty (or null) changeset (i.e. they have no common root revision). It can get a little messy to track the branches but it does work.
For example:
hg init
hg branch project-1
# Changes, commits, repeated as needed
hg update null
hg branch project-2
# Changes, commits, repeated as needed
You now can see all your projects:
> hg branches
project-2 5:42c2beffe780
project-1 2:43fd60024328
The projects are unrelated (though you can merge them):
> hg debugancestors
-1:000000000000
Most usefully: you can clone only the project you want, and the others won't mix in:
> hg clone <repository> -r project-1
The graph for this would look something like this (hg log -qG):
# 5 | project-2 | {tip}
|
o 4 | project-2
|
o 3 | project-2
o 2 | project-1
|
o 1 | project-1
|
o 0 | project-1
You can do this for as many projects as you need, listing each with hg branches, and jumping between them with hg update. This takes some care, because named branch support isn't perfect. It isn't always intuitive for one thing (read about hg clone -u in Mercurial 1.4 -- the pre-1.4 behavior is surprising when cloning). But it does work.

Mercurial and Git only permit cloning on the entire repository. Thus it is recommended that each project gets its own repository.
Mercurial has a forest extension to ease having a "forest" for project repositories. The extension keeps each project in a separate repository, but provides options to update/push/pull all the forest repositories together.

It's possible to ask Mercurial to clone just a branch using hg clone -r branchname (see Mercurial clone from a branch).
With Google's NarrowHG extension extension it's possible to perform a narrow clone (see How do I clone a sub-folder of a repository in Mercurial? for a similar question).

I know that it is nearly 10 years after this question was asked, I I stumbled across
this question by accident.
There is a new mercurial extension call sparse that allows you to do this.

Here's a possible improvement to Vadim Kotov's solution that supports spaces in the small-project name/subfolder:
#echo off
echo Converting "%~1"
REM Create the file map
echo include "%~1" > ~myfilemap
echo rename "%~1" . >> ~myfilemap
REM Run the convert process
hg convert --filemap ~myfilemap .\ "..\%~1"
REM Delete the file map
del ~myfilemap
cd ".\%~1"
REM update the new repo--to create the files
hg update

Related

How can I create a branch for a non-tip revision in Mercurial?

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.

Move exising hg repository into new hg repo, preserving history [duplicate]

This sounds kind of complex, so let me explain:
Project_A has lived for some time within its own Mercurial repository. Project_A is now getting folded into a new super project, Super-Project_B. Super-Project_B also has a mercurial repository. We would prefer if Project_A were not a subrepo, but instead just a normal child, but we also don't want to lose the history. Is there a way to do this?
Yeah. Use the convert extension to move projectA down one directory level:
hg convert --filemap filemap.txt projectA projectA-redone
where your filemap.txt has this line in it:
rename . projectA
(that dot might be a slash but I don't think so).
That will give you a new repo, projectA-redone, that has all the history of A though all the changesets will have different hashes since their content (paths) have changed to get "projectA" in front of all of them.
Then you go into Super-Project_B and do a hg pull -f /path/to/projectA-redone. You need the -f because otherwise you'll be told that the repos are unrelated since they have no changesets in common.
Finally you'll do a hg merge in Super_project_b which should have no conflicts (unless you already had a projectA directory, in which case you should've picked a different name or hg removed it first).
After doing that B will have all of A inside the projectA subdirectory and all history will be intact.

Using Mercurial (hg), is there a way to diff rev 4873 and 4821 but only for my changes?

I want to know what changes I made, without looking at the 30 other files that other team members modified.
So when I hg out, it said the first changeset to push was 4821, so since then I have pulled, merged, and pushed a couple times. Now I want to make sure all the debugging code is removed. Is there a way to diff the current revision (4873) against revision 4821 but only for my changes?
If your changes are in different files than those of your coworkers, which is how it sounds, you can use something like this:
hg diff -r 4821 -r 4863 -I path/to/file1 -I path/to/file2
If they're mixed in the same files as other people's changes then you would have needed to keep your changes in a separate branch (which doesn't require the branch command, anonymous branching is commonly used for this sort of thing).
The following command should do the trick:
hg diff -r "4821:4873 and user(your_username)"
I don't know if you can upgrade to the recently release Mercurial 1.6 or not, but this functional query language feature they just put in is what you might be looking for.
Try this approach:
First clone your local repo to another folder
In the new clone, rebase your last changeset so that it immediately follows your the other changeset (this should create a new head from it)
Do the diff

How can I import a mercurial repo (including history) into another mercurial repo as a subdirectory, without using subrepos?

This sounds kind of complex, so let me explain:
Project_A has lived for some time within its own Mercurial repository. Project_A is now getting folded into a new super project, Super-Project_B. Super-Project_B also has a mercurial repository. We would prefer if Project_A were not a subrepo, but instead just a normal child, but we also don't want to lose the history. Is there a way to do this?
Yeah. Use the convert extension to move projectA down one directory level:
hg convert --filemap filemap.txt projectA projectA-redone
where your filemap.txt has this line in it:
rename . projectA
(that dot might be a slash but I don't think so).
That will give you a new repo, projectA-redone, that has all the history of A though all the changesets will have different hashes since their content (paths) have changed to get "projectA" in front of all of them.
Then you go into Super-Project_B and do a hg pull -f /path/to/projectA-redone. You need the -f because otherwise you'll be told that the repos are unrelated since they have no changesets in common.
Finally you'll do a hg merge in Super_project_b which should have no conflicts (unless you already had a projectA directory, in which case you should've picked a different name or hg removed it first).
After doing that B will have all of A inside the projectA subdirectory and all history will be intact.

Mercurial Remove History

Is there a way in mercurial to remove old changesets from a database? I have a repository that is 60GB and that makes it pretty painful to do a clone. I would like to trim off everything before a certain date and put the huge database away to collect dust.
There is no simple / recommended way of doing this directly to an existing repository.
You can however "convert" your mercurial repo to a new mercurial repo and choose a revision from where to include the history onwards via the convert.hg.startrev option
hg convert --config convert.hg.startrev=1234 <source-repository> <new-repository-name>
The new repo will contain everything from the original repo minus the history previous to the starting revision.
Caveat: The new repo will have completely new changeset IDs, i.e. it is in no way related to the original repo. After creating the new repo every developer has to clone the new repo and delete their clones from the original repo.
I used this to cleanup old repos used internally within our company - combined with the --filemap option to remove unwanted files too.
You can do it, but in doing so you invalidate all the clones out there, so it's generally not wise to do unless you're working entirely alone.
Every changeset in mercurial is uniquely identified by a hashcode, which is a combination of (among other things) the source code changes, metadata, and the hashes of its one or two parents. Those parents need to exist in the repo all the way back to the start of the project. (Not having that restriction would be having shallow-clones, which aren't available (yet)).
If you're okay with changing the hashes of the newer changesets (which again breaks all the clones out there in the wild) you can do so with the commands;
hg export -o 'changeset-%r.patch' 400:tip # changesets 400 through the end for example
cd /elsewhere
hg init newrepo
cd newrepo
hg import /path/to/the/patches/*.patch
You'll probably have to do a little work to handle merge changesets, but that's the general idea.
One could also do it using hg convert with type hg as both the source and the destination types, and using a splicemap, but that's probably more involved yet.
The larger question is, how do you type up 60GB of source code, or were you adding generated files against all advice. :)