How to clone topics in mercurial? - mercurial

Background: Mercurial Topics
Mercurial has a nice feature call topics as part of the evolve extension. These act as temporary lightweight local branches, and are an integral part of the Heptapod workflow, ensuring nice interactions with Git (via hg-git) for example. They are enabled by include the following in your ~/.hgrc file (or per-repo in .hg/hgrc):
# ~/.hgrc
...
[extensions]
evolve =
topics =
As these are designed for local work, when you push, the topics are not pushed to the server (but become temporary branches in git with the Heptapod workflow).
Question
How can I clone a repo locally to get the topics in my clone?
Part of the answer is to set the source repo to non-publishing: (One should probably do this in the cloned repo to after cloning).
# source_repo/.hg/hgrc
[phases]
publish = false
This keeps the draft phase of the changesets that are part of the topics.
Update: With python==3.9.7, mercurial==6.0.1, and hg-evolve==10.4.1 or higher, this seems to be sufficient. As #Craig points out in the comments, the original issue might have been because I was making the first commit a topic, but this is no longer an issue.
MWE (Old... This seems to work now.)
mkdir a
cd a
touch A.txt
hg init
hg add A.txt
hg topic "A"
hg commit -m "Initial commit of A"
cat > .hg/hgrc <<EOF
[phases]
publish = false
EOF
cd ..
hg clone a b
Now in a, there is a topic A and the commit is in the draft phase (shown by orange colour in the output):
$ hg log -v
changeset: 0:62c4... # orange, indicating draft phase
tag: tip
topic: A
user: Michael <...>
date: Wed ...
files: A.txt
description:
Initial commit of A
while in b, everything is the same, including the draft phase, but no topic:
$ hg log -v
changeset: 0:62c4... # orange, indicating draft phase
tag: tip
user: Michael <...>
date: Wed ...
files: A.txt
description:
Initial commit of A

Consider this a workaround, perhaps, but it should work.
If you're just working locally, you don't actually have to clone. You could just make a full copy of the entire working folder (the folder which contains the .hg folder).
Copy it anywhere you want, then when you run Mercurial commands within that path it will behave exactly like it was cloned, except it won't see your original as the default publishing repo, but instead the same one as your original. You can obviously change this in the .hgrc file if you need to.
Depending on your needs, you might want to do an hg up null beforehand to clear all the local working files, which will speed up the copy for a large repo.

It seems like this was a localized bug. Making the source repo publishing seems to be enough.
# source_repo/.hg/hgrc
[phases]
publish = false

Related

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.

switch branches in hg while preserving files

While developing my software i create a set of test-cases which an be automatically done via make tests. To keep things organized i put those in a sub-directory tests. To keep my commits organized as well and not to clutter my development branches, i created an extra branch tests where i commit the test units.
My problem is: when i commit the units to the test branch and switch back to development, said tests are deleted
D:\Project>hg branch test
D:\Project>edit...
D:\Project>hg add
D:\Project>hg commit
D:\Project>hg up dev
2 files updated, 0 files merged, 3 files removed, 0 files unresolved
How can i preserve those files?
(i tried the solution for Mercurial: Switch working directory to branch without losing changes? but it still deletes the files)
EDIT See my own answer below
Those test files don't exist in your development branch, so when you check out the development branch they won't be in the working directory. DVCSs like Mercurial and git don't let you have one directory checked out at one revision/branch and another one at another revision/branch.
The answers to which you linked are for bringing the changed and their files over into another branch, they're not what you're asking for.
So the way you want to do it (separate branches) isn't going to work, but there are plenty of other/better options
One choice would be to make your tests a patch that's managed by a mq (Mercurial Queues) repository, which can itself be versioned. Then when you where in your dev branch and wanted to run the tests you'd do:
hg qpush # tests show up
... run tests, edit tests ..
hg qrefresh # save the changes you made to the test
hg qpop # tests vanish again
MQ is poweful, but sometimes a little hard to wrap your head around.
Another choice would be to make your tests a parent repository and your actual code a child repository of that parent. Your disk layout would look like:
parent/
tests/
existingrepo/ # <-- the repo you already have w/o tests
Then people could clone and you could push the existingrepo w/o tests, but the outer repo would include a pointer to it and the two would be versioned in lockstep. Again, sort of tricky, but has some nice results.
The third option, and my preferences is to get over the "To keep my commits organized as well and not to clutter my development branches" mentality. Tests are just as important as the primary code, they should be versioned with the code, and they're not cluttering anything, they're providing valuable tools to help comprehend what the code is doing. You can always use hg log --exclude tests/ to see a history that excludes them when that's convenient.
To take that plunge just do:
hg update development
hg merge tests
and you're good to go.
when i commit the units to the test branch and switch back to development, said tests are deleted
It's expected and correct result: branches store diverged lines of development, you add something only in test branch, before merge this data will exist only in branch on creation
In order "To keep my commits organized as well and not to clutter my development branches...", but have tests in development branch you can use at least two ways:
Periodically merge tests branch to development (all changes in tests will appear in development), but use log only for changesets in development branch, maybe without mergesets from tests. It's rather simply revset, you can even write log command with revset into aliases and use aliased command when needed
As Ry4an mention, you can instead of branch-separtion, perform repository-separation with subrepo|guestrepo technique - if tests are stored in subdir of main repo, convert /tests into nested repository -> subrepository and have two independent, but linked repos (future reading: Subrepository in Mercurial wiki and Subrepositories in Mercurial Kick Start Exercises)
to achive what i wanted i eventually created a subdirectory for the repo:
project/
main.cpp
makefile
.repo/
.hg/
and created a new make target:
REPO := .repo
init: $(REPO) $(REPO)/.hg
$(REPO):
mkdir -p $(REPO)
$(REPO)/.hg:
hg init $(REPO)
commit : | $(REPO) $(REPO)/.hg
ifdef BRANCH
hg -R $(REPO) update -C $(BRANCH)
endif
find . -regex "^\./[^.].*" -exec cp --parents {} ./$(REPO) \;
# the regex prevents "hidden" dot-folder from copying
# one could use --link to save time and drive usage/space
# but i am concerned about hg auto merging and overriding (per hardlink)
# my actual changes
hg -R $(REPO) commit $(EXTRA)
so i could just issue make commit BRANCH=tests to commit to arbitrary branch without losing all changes not relevant to the branch. Implementing of hg add as wipe .repo, copy file there hg -R .repo add $(filename) is left as exerciser for the reader

Mercurial requiring manual merges unexpectedly

I've got a project running under Mercurial and am finding a lot of situations where a file needs manually merging, when I believe it should be able to merge automatically. I am wondering whether there are any options that can be given to Mercurial to help it out in these areas.
The project has an underlying platform with a couple of hundred files that can't be edited on the project. When the platform is updated, the project gets updated versions of these core files outside of Mercurial. The sequence I'm seeing repeatedly is:
On central dev system (linked to the core platform update mechanism):
Get a new version of core platform.
Commit these changes e.g. hg commit -m "New platform release"
Push to central mercurial server
On my Linux box:
Commit local changes
Pull from central mercurial server, and try to merge
Find merge conflicts on core files
The last two core files I've had to merge have no changes between the base and local versions (the access time is updated during a build, but the content is the same). The only changes are on the remote revision I'm merging with.
The only non-standard configuration I'm aware of is that the central mercurial instance is running under Rhodecode, with a commit hook setup to update a Redmine repository.
Is there anything else that can be configured in mercurial to help it figure out merges?
You can redo a merge with --debug to get more information about a merge. That is, take your repository and do
$ cd ..
$ hg clone my-project -r 123 -r 456 merge-test
where 123 and 456 is the two parents of the merge you want to examine closer. Then run
$ hg merge --debug
to see what Mercurial says. It should look like this if the file foo has only been changed in the branch you're merging in:
$ hg merge --debug
searching for copies back to rev 2
resolving manifests
overwrite: False, partial: False
ancestor: 932f5550d0ce, local: b0c286a4a76d+, remote: c491d1593652
foo: remote is newer -> g
updating: foo 1/1 files (100.00%)
getting foo
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Here I was on revision b0c286a4a76d and merged with c491d1593652.
You can also use
$ hg status --rev "ancestor(b0c286a4a76d, c491d1593652)" --rev "c491d1593652"
M foo
$ hg status --rev "ancestor(b0c286a4a76d, c491d1593652)" --rev "b0c286a4a76d"
M bar
to double-check which files have been changed between the ancestor revision and the two changesets you're merging. Above you see that I changed foo on one branch and bar on the other.
If you see a platform file appear in both status lists, well then something went wrong in your procedures and this can explain the merge conflicts.
If this isn't enough to figure out what went wrong, then I suggest asking this question on the Mercurial mailinglist. That's a great place for discussion and bug-hunting — much better than Stack Overflow.

Freshly cloned hg repository shows files as modified (Windows)

I've freshly cloned a Hg repository on Windows XP and hg status reports a lot of (all?) files as Modified. What could be the reason?
E:\myprojects\myproject>hg summary
parent: 206:03856faec803 tip
latest commit message
branch: default
commit: 78 modified
update: (current)
Even after having performed hg revert --all --no-backup, hg diff --git reports:
diff --git a/path/to/file1 b/path/to/file1
--- a/path/to/file1
+++ b/path/to/file1
## -1,332 +1,332 ##
-line1
-line2
...
-line332
+line1
+line2
...
+line332
diff --git a/path/to/file2 b/path/to/file2
--- a/path/to/file2
+++ b/path/to/file2
## -1,231 +1,231 ##
...
line231
\ No newline at end of file
If hg revert isn't able to restore a clean working directory, this most likely means that the repository doesn't contain the files in canonical form, but hg status gets them into canonical form before comparing against the repository contents. Usually this happens when eol-extension is turned on, but the repository contains files with CRLF. To be sure, turn eol-extension temporarily off and check whether the files are still modified. If so:
(1) turn it on again and commit the changes, so they will be in canonical form in the repository, too.
(2) make sure that other users accessing your repository have eol-extension turned on, otherwise this will be a never ending game :)
Try hg diff --git that will shows you what Mercurial thinks is modified about the files. My guess is the execute bit permission.

Mercurial Patch Creation and Usage

I have come across a problem that I "think" can only be resolved using patches.
I cloned a project from our main repository, made quite a few changes (updates, deletion of files & directory and additions) to it. These changes are not even committed. The problem is, project from the main repository has been deleted/removed and recreated as a new project (name is same, all the directory structures everything is same as before). I cloned that project again from the main repository and would like to transfer all my uncommitted changes to it.
I am still exploring the hg patch to resolve that. It would be helpful if someone could confirm that creating and adding a patch IS the right approach to this, any resources explaining the process would be of great help.
You're correct — a patch is what you need to transfer the information from one repository to another (unrelated) repository. This will work since the files are the same, as you note.
So, to transfer your uncommitted changes from your old clone, you do
$ hg diff -g > uncommited.patch
$ cd ../new
$ hg import --no-commit ../old/uncomitted.patch
That will restore the information saved in the patch. This includes information about files that are added or renamed in the old clone.
The following steps can be performed with a standard Mercurial install:
Commit the changes in your local repository. Note the revision number.
Use "hg export -r REV >patch.diff" to create a patch.
Clone the new repository.
Use "hg import patch.diff" to apply the patch to the new repository.
Example
C:\>hg init example
C:\>cd example
C:\example>echo >file1
C:\example>hg ci -Am file1
adding file1
C:\example>hg clone . ..\example2
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\example>rd /s/q .hg
C:\example>hg init
C:\example>hg ci -Am same-but-different
adding file1
At this point example and example2 have identical contents, but the repositories are unrelated to each other due to deleting and reinitializing the .hg folder.
Now make some changes and commit them in one of the repositories, then export them as a patch:
C:\example>echo >>file1
C:\example>echo >file2
C:\example>hg ci -Am changes
adding file2
C:\example>hg export -r 1 >patch.diff
Below shows that the other repository can't pull the changes, because of the reinitialization. It can, however, apply the patch successfully:
C:\example>cd ..\example2
C:\example2>hg pull
pulling from c:\example
searching for changes
abort: repository is unrelated
C:\example2>hg import ..\example\patch.diff
applying ..\example\patch.diff
I would first make copies of everything so you have a way of backtracking.
Then, in the working copy with the changes, I would first delete the .hg directory, then copy in the .hg directory from the new repo. This basically transfers all of the changed files into the new repo without the need to delete any files and directories.
You will still need to tell the repo about whether to remove any files marked as missing. You will also have to handle renames manually. If this is a small number of operations, it's easier than trying to use the patch method.
Once this is done, commit your changes and push, if necessary.
seems like what you want is patch queues. In that you have uncommitted changes, and you want to pull from the new repo before committing them....
$ hg qinit -c # initialize mq for your repo containing the uncommitted changes
$ hg qnew name_of_patch # create patch that contains your uncommitted changes
$ hg qpop # resets your working dir back to the parent changeset
no worries though, your changes are safe and sound in .hg/patches/name_of_patch to see for yourself.....
$ cat .hg/patches/name_of_patch
now pull in the new repo
$ hg pull -u http://location.of.new/repo # pull in changes from new repo update working dir
$ hg qpush # apply your uncommitted changes to new repo
If you are lucky you will have no merge conflicts and you can go ahead and commit the patch by....
$ hg qfinish -a # change all applied patches to changeset
And then if you want....
$ hg push http://location.of.new/repo
If the repos are unrelated, just init a patch repo on your new repo. and manually copy the patch in and add it to .hg/patches/series file.
assuming patch was created. clone new repo
$ hg clone http://location.of.new/repo ./new_repo
init patch repo
$ cd ./new_repo && hg qinit -c
copy patch
$ cp ../old_repo/.hg/patches/name_of_patch .hg/patches/
edit series file using an editor of some sort
$ your_favorite_editor .hg/patches/series
name_of_patch # <---put this in the series file
apply your patch to new repo
$ hg qpush
if no merge conflicts and you are convinced it works
$ hg qfinish -a
If the layout is the same, you can just copy all the files over (excluding .hg) and then use hg addrem.
Try to look into the MQ plugin, it does exactly this if I recall. I've never had a use for that though, so I can't say.
If the old repository was simply moved/cloned to a new URL then you could simply change the remote repository you talk to the new one.
If, however, it was recreated from the ground up (even with the same structure) then I don't believe Mercurial has any built-in functionality to help you here. Mercurial patches reference specific changesets which won't exist in your new repository.
You could use a merge tool to perform the diff and bring across any changes you made.
Edited To answer the question in the comment:
When you clone the repository you are taking a complete snapshot of the entire change history - along with the associated change-set IDs, etc.
Mercurial tracks changes by change-sets to the repository, rather than at the file level like Subversion.
If you clone, then you can easily push/merge into another repository that was also cloned from the same source.
If you recreated the repository then the change IDs won't match, and can't be merged in Hg.
The only option in this scenario would be to use a Merge tool which will let you see mismatches in files/folder structure.
Also: Worth pointing out http://hginit.com/ because it explains (indirectly) some of this.