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].
Related
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.
In Git it is easy, because remote/branch is pointing to a different commit than branch. How to do it with Mercurial?
If you mean seeing what's different between your local repo and the one you're pushing to, try
hg outgoing
Since Mercurial 2.1, there is also a purely local solution: phases. The draft phase is probably what you are looking for. For details, refer to:
https://www.mercurial-scm.org/wiki/Phases
You may find hg phase <rev> and hg log -r "draft()" interesting.
There is an remotebranch extension that will give you a Git-like setup. It tracks the remote heads for the repositories listed in the [paths] and exposes them as tags named <path>/<branch>. This lets you run
$ hg diff -r foo/default
to see what has changed since the default branch in the foo repository. There is also new revset keywords that let you do things like
$ hg log -r "not pushed()"
to get what
$ hg outgoing
would do, but without any network traffic.
What's the command line call to show all revisions in the draft phase?
hg log --style phases
That will display the log + the phase, since Mercurial 2.7 (2013-08-01).
I'd just use hg outgoing as others are suggesting, but hg summary will tell you too. It may require the --remote option to have it check the remote default server.
If you need to select the changesets for further processing, then you can use the outgoing revset predicate. This lets you re-implement hg outgoing as
hg log -r "outgoing()"
but the real benefit is that you can use this in other contexts, such as
hg strip "outgoing()"
I've looked for that in the manual, but I can't generate a patch for the last commit.
I tried
hg qnew patch_name
but it does only file with
# HG changeset patch
# Parent a6a8e225d16ff5970a8926ee8d24272a1c099f9c
I also tried
hg export tip
but it doesn't do anything. I committed the changes exactly.
How to generate a patch file with the last commit in?
The command to do this is export:
$ hg export -o FILE -r REV
It doesn't require redirection and will thus work correctly on any platform/shell.
Your hg export tip is the best way to do it, and the hg diff and hg log based answers are just lesser versions of the same. What exactly do you see/get when you type hg export tip? What does the output of hg log -p -r tip show?
The changeset tip is just means "the changeset that most recently arrived in my repository" which isn't as useful a concept as you might think, since hg pull and hg tag all create changesets too. If you really want the last thing you committed you'll need a more precise revspec.
Like so:
hg diff -r tip > tip.patch
You can use this command:
hg log -r tip -p > tip.patch
this will generate a patch for just that revision.
If you want to convert the latest commit to a patch file, use
hg qimport -r tip
This will replace the topmost regular commit with an applied MQ patch file.
To generate patches using "mq extensions" in mercurial, you can follow the below given steps. This will create a patch using mercurial:
1) Enabling mq extensions: Add the following lines to your hgrc file and save it.
[extensions]
mq =
2) Creating a patch using mq extensions: To create a patch using mq extensions you can do the following.
hg qnew -e -m "comment you want to enter" bug_name.patch
In the above command, -e flag is for editing the patch and -m flag is for adding a message to the patch.
3) Updating the patch: For updating the patch, you can use the following command when a patch is already applied.
hg qrefresh
is this possible with Mercurial? and which Version Control system can do this besides Clearcase?
David is correct that you can't have a branch that exists on only a single file, but it's worth pointing out that people often have branches that alter only a single file. Since the branch metadata is stored in the changeset, and since the changeset contains only a delta (change), having a branch that alters only a single files is nearly instantanous to create, update, commit, and merge, plus it takes up almost no space on disk.
Resultingly, it's a very common way to handle per-customer configurations. Keep the tiny change for them in a branch, and merge from main, where development happened, into that branch, whenever you want to update their deployment.
How you could use MQ:
$ hg qnew -m "Changes for client0" client0
... change the file ...
$ hg qref # update the client0 patch with the changes
$ hg qpop # pop the changes off the queue stack
... develop like normal ...
... client0 asks for a build ...
$ hg qpu # apply client0's patch
$ make release
$ hg qpop
It would get a bit finicky if you've got to deal with a lot of clients… But it may be worth considering.
The other thing you could do, of course, is just commit a bunch of .diff files:
... make changes for client 0 ...
$ hg diff > client0.diff
$ hg revert --all
$ hg add client0.diff
$ hg ci -m "Adding client0 changes"
... develop ...
... client0 asks for a build ...
$ patch -p1 < client0.diff
$ make release
$ hg revert --all
No, it's not possible. A branch in Mercurial is a snapshot of the entire repository state.
You could do it with CVS, though, as CVS tracks changes on a per-file basis :)
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.