hg command for git bundle - mercurial

In git, there is a command 'git bundle' which bundles a git repository into 1 big file.
Is there the equivalent command for 'hg'?

Hg supports the same command. Here's the help for it:
U:\>hg help bundle
hg bundle [-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]
create a changegroup file
Generate a compressed changegroup file collecting changesets not known to
be in another repository.
If you omit the destination repository, then hg assumes the destination
will have all the nodes you specify with --base parameters. To create a
bundle containing all changesets, use -a/--all (or --base null).
You can change compression method with the -t/--type option. The available
compression methods are: none, bzip2, and gzip (by default, bundles are
compressed using bzip2).
The bundle file can then be transferred using conventional means and
applied to another repository with the unbundle or pull command. This is
useful when direct push and pull are not available or when exporting an
entire repository is undesirable.
Applying bundles preserves all changeset contents including permissions,
copy/rename information, and revision history.
options:
-f --force run even when the destination is unrelated
-r --rev a changeset intended to be added to the destination
-b --branch a specific branch you would like to bundle
--base a base changeset assumed to be available at the destination
-a --all bundle all changesets in the repository
-t --type bundle compression type to use (default: bzip2)
-e --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
The command for the reverse operation is hg unbundle.

Related

Mercurial command to probe remote file size

Is there a way, preferrably builtin mercurial command, to get some information about remote files/directory before pulling to local fs?
EDIT: The initial wording may have been somewhat unclear; I'd like to find out how big the repository is, how much storage space will it occupy/need. Is it possible to get that info from the manifest file? Essentially, Is there a way, preferrably builtin mercurial command, to get some information about remote files/directory before cloning to local fs?
hg incoming -h
hg incoming [-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]
aliases: in
show new changesets found in source
Show new changesets found in the specified path/URL or the default pull
location. These are the changesets that would have been pulled if a pull
at the time you issued this command.
For remote repository, using --bundle avoids downloading the changesets
twice if the incoming is followed by a pull.
See pull for valid source format details.
Returns 0 if there are incoming changes, 1 otherwise.
options:
-f --force run even if remote repository is unrelated
-n --newest-first show newest record first
--bundle FILE file to store the bundles into
-r --rev REV [+] a remote changeset intended to be added
-B --bookmarks compare bookmarks
-b --branch BRANCH [+] a specific branch you would like to pull
-p --patch show patch
-g --git use git extended diff format
-l --limit NUM limit number of changes displayed
-M --no-merges do not show merges
--stat output diffstat-style summary of changes
-G --graph show the revision DAG
--style STYLE display using template map file
--template TEMPLATE display with template
-e --ssh CMD specify ssh command to use
--remotecmd CMD specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts
config)
-S --subrepos recurse into subrepositories
[+] marked option can be specified multiple times
use "hg -v help incoming" to show more info

Add a parent to the original changeset in Mercurial

I have a project with 24 months of source control history in a Mercurial repository.
I've recently found some old tarballs of the project that predate source control, and i think they would be useful to import into the repository as "pre-historic" changesets.
Can i somehow add a parent to my initial commit?
Alternatively, is it possible to re-play my entire repository history on top of the tarballs, preserving all metadata (timestamps etc)?
Is it possible to have the new parent commits use the timestamps of these old tarballs?
You can use the convert extension to build a new repository where the tarballs are imported as revisions before your current root revision.
First, you import the tarballs based on the null revision:
$ hg update null
$ tar -xvzf backup-2010.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2010'
$ rm -r *
$ tar -xvzf backup-2011.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2011'
I'm using addremove above to give Mercurial a chance to detect renames between each tarball (look at the --similarity flag to fine-tune this and use hg rename --after by hand to help Mercurial further). Also, I remove all the files in the working copy before importing a new tarball: that way the next commit will contain exactly the snapshot present in the tarball you unpack.
After you've imported all the tarballs like above, you have a parallel history in your repository:
[c1] --- [c2] --- [c3] ... [cN]
[t1] --- [t2] --- [tM]
Your old commits are c1 to cN and the commits from the tarballs are t1 to tM. At the moment they share no history — it's as if you used hg pull -f to pull an unrelated repository into the current one.
The convert extension can now be used to do a Mercurial to Mercurial conversion where you rewrite the parent revision of c1 to be tM. Use the --splicemap flag for this. It needs a file with
<full changeset hash for c1> <full changeset hash for tM>
Use hg log --template '{node} ' -r c1 -r tM > splicemap to generate such a file. Then run
$ hg convert --splicemap splicemap . spliced
to generate a new repository spliced with the combined history. The repository is new, so you need to get everybody to re-clone it.
This technique is similar to using hg rebase as suggested by Kindread. The difference is that convert wont try to merge anything: it simply rewrites the parent pointer in c1 to be tM. Since there is no merging involved, this cannot fails with weird merge conflicts.
You should look at using rebase. This can allow you to make the changes the 2nd changeset on your repo ( you have to rebase from the 1st ).
https://www.mercurial-scm.org/wiki/RebaseExtension
However, note that if there are other clones of this repo existing ( such as for fellow developers, or on a repo server ), you will have issues with them pulling the revised repo. You will probably have to co-ordinate with the owners of those clone's to get all work into a single clone, rebase that clone, and then have everyone re-clone from the revised clone. You will also have to change the phase the of the changesets.
https://www.mercurial-scm.org/wiki/Phases
Honestly though, I would just add them to your 'modern-day' repo, I don't think making them pre-historic would give you any notable advantage over adding them to the top.

I want to create a versioned folder as a result of mercurial "hg clone <repo url>"

When cloning a repository in Mercurial, is there a way to create a target folder based on the latest changeset? Example:
$ hg clone http://hg.repo.com:8000/myrepo 'myrepo-$VERSION'
The folder should be named after the version of the project, e.g., myrepo-1.3.
If you are okay with using the changeset hash, then you can start with
$ hg identify -i http://hg.repo.com:8000/myrepo
to get the ID of the tip changeset. You can combine this with clone like this in a Unix shell:
$ hg clone http://hg.repo.com:8000/myrepo \
"myrepo-$(hg -i identify http://hg.repo.com:8000/myrepo)"
To make it more convenient to use, I would create an alias for this:
[alias]
vclone = !DEST=$(basename "$1")-$($HG identify -i "$1");
echo "destination directory: $DEST";
$HG clone "$1" "$DEST"
This let's you do hg vclone foo to get a foo-<ID> clone.
In the alias I took care of computing a suitable basename from the clone URL and to print the destination in the same way that hg clone normally does when you don't give an explicit destination. Finally, I took care to quote the arguments so that you can clone a directory with spaces in the name. (If you also have characters like " in your names, well then you're out of luck without better support for quoting in Mercurial shell aliases.)
You'll have to first clone it into some temporary folder, then inspect the repo's tip to see its revision or whatever trait you want to use in your naming scheme and then rename the previous (temporary) location to whatever it should be now ... it's not available in vanilla Hg to my knowledge.

Mercurial: Include secret changesets in bundle?

Using Mercurial, how can I bundle all changesets not known to be in another repository, including secret changesets?
I know bundle's --base option happens to include secret changesets, but I don't want the --base behavior. (And it seems unusually weird that secret changesets are always included with --base but are never included without it. Shouldn't there be a separate option?)
FYI, I commonly want to make a backup of all changesets which are only in my local repo before attempting a potentially dangerous history rewrite.
You are correct that hg bundle will normally exclude secret changesets. This is because it's just running the equivalent of hg outgoing and bundling these changesets.
So some work-arounds:
If you know that you have at least one draft or public outgoing changeset as an ancestor of your secret changesets, then you can use
$ hg bundle --base "parents(outgoing())"
to get what you want. The outgoing() revset will pick the missing draft and public changesets and parents(outgoing() will be suitable bases. Since you use --base you get all descendants (public, draft, and secret) from these bases.
You could temporarily make your secret changesets draft, bundle, and then mark them secret again:
$ secret=$(hg log --template "{rev} " -r "secret()"); \
hg phase -d $secret; \
hg bundle out.hg; \
hg phase -f -s $secret
(I use Zsh and there I had to use ${=secret} instead of $secret because Zsh doesn't do word splitting on parameter expansion by default.)
It's important to chain the commands with ; instead of && since you'll want to reset the phases regardless of what happens in the hg bundle call — passing wrong parameters to hg bundle should not mean that you lose all the information about the secret changesets. Note also that since secret changesets only have secret descendants, there's no information loss with this technique.
You can turn this into a shell alias:
[alias]
bundle-all = !secret=$(hg log --template "{rev} " -r "secret()");
hg phase -d $secret;
hg bundle $#;
hg phase -f -s $secret
The $# is expanded by Mercurial before the alias is invoked and this lets you insert the necessary arguments for hg bundle.
Note that phase information cannot be stored in bundles — the bundle format has not been changed to accommodate it.
If you know there is at least one public changeset, you can use this:
hg bundle -r "not public()" --base public()
OTOH, that won't work if there are no public changesets, use this instead:
hg bundle -r "not public()" --base null
The problem with Martin's answer is that it relies on outgoing, which in turn relies on a direct connection to the push repo. If you don't always have an internet connection to that repo, these methods work well for me. It's also somewhat simpler than the phase dance.
One test for whether there are any public changesets is to capture the output of:
hg log -r public() -l 1 --template "{rev}"
and test its length, or the presence of [0-9].

Get tip changeset of remote Mercurial repository

My .hg/hgrc file has the line:
default = http://some/remote/repository
Is there a quick command to print the tip revision of that repository (which may or may not be inside my local repository)?
You can use the identify command like this:
$ hg identify $(hg paths default)
This is one of the few commands that can operate on a remote repository. If you need more information about the remote repository, then I suggest you take a look at hg incoming.
The following returns the latest changeset number (tip) of a remote repository:
hg identify --id http://www.myrepo.com
hg id default
This is a shorter form of "hg identify $(hg paths default)".