I have a repository with 5 sub-directories.
repo/
a/
file-a
b/
file-b
c/
file-c
d/
file-d
e/
file-e
How can I convert each sub-directory in to different repositories? (Only relevant changesets to the given sub-directory should be converted new repositories)
Use the convert extension and the --filemap flag (after making sure that the convert extension is enabled in your ~/.hgrc):
$ hg init new-a
$ cat > a-map
rename a .
exclude b
exclude c
...
$ hg convert --filemap a-map old-repo new-a
$ cd new-a
$ hg co tip
Related
Mercurial tracks the contents of a file throughout renames (hg mv $OLD $NEW), so that hg annotate $NEW also shows up the line-wise changes formerly made to $OLD with their original identification. That works fine.
BUT there seems no straightforward way to find out the name of the $OLD file, to which some given line has belonged within the ancestry of $NEW. hg annot $NEW -r$REV only works down to the rename changeset.
Of course the information is somehow accessible, e. g. by crawling through hg log (without --follow) and identifying the renames with some hg log -r$RENAMEREV -g -p (or by clicking through hg serve's web interface).
But this “workflow” is not only annoying and error-prone, but [most importantly] it isn't non-interactive/scriptable.
My question: Is there a way to get/construct either
some list of the file name history of $NEW (best with respective revision ranges), or
the name of the file in which line $LINE was commited (some kind of filename option for hg annot)?
Ideas in either the hg CLI or Python/hglib appreciated.
Either include the {file_copies} keyword in your hg log template:
$ hg init demo
$ cd demo
$ touch a
$ hg ci -Am 'file a'
adding a
$ hg mv a b
$ hg ci -Am 'moved to file b'
$ hg log -r . -T"{file_copies}\n"
b (a)
The built-in template status will include file copy info when you set the --copies flag:
$ hg log -r 1 -Tstatus --copies
changeset: 1:b37952faaddc
tag: tip
user: Martijn Pieters <mjpieters#fb.com>
date: Sun Jul 31 16:07:04 2016 +0100
summary: moved to file b
files:
A b
a
R a
So file b was taken from a.
See hg help template for more things you can include in log output.
The format of the hg mv command is hg rename [OPTION]... SOURCE... DEST
. Path names are relative to the current directory. Thus, when you are at a command prompt at the root directory and specify hg mv -n -I * A\B Z, mercurial will create the directory Z under the root directory, and move A\B\readme.txt to Z\readme.txt.
How can you specify, under Windows, that Z is the repository root directory? I tried using '.' as destination, i.e. hg mv -n -I * A\B . but got a message that A\B\readme.txt will be copied to B\readme.txt, not to readme.txt at the root. I tried using '~' as the destination, but hg mv -n -I * A\B ~ got me a new directory named "~" below the root, obviously not what I wanted.
So my question is: How do I specify the repository root directory as the destination to the mercurial move command?
edit: I'll try to clarify the issue.
I have an OldDev repository containing two products: Product-A and Product-B. Using the '~' symbol to denote OldDev's root folder, OldDev contains two folders: ~/Product-A and ~/Product-B (in addition, of course, to ~/.hg where its metadata is stored).
Each product is composed of a few projects, and each such project is assigned a folder under the product's folder. Thus Product-A has the Project-A, Project-B and Project-C, stored in ~/Product-A/Project-A, ~/Product-A/Project-B and ~/Product-A/Project-C, correspondingly. ~/Product-A/Project-A/xxx.cs is one of (Product-A's) Project-A's files.
Now I want to extract Project-A to its own NewDev repository. As it's the single project in NewDev, it makes no sense to retain the product/project hierarchy, so I want it to be at the root of NewDev: it xxx.cs file, for example, will be #/xxx.cs, where # is the root folder of NewDev (the one contianing NewDev's .hg directory where NewDev's metadata is stored).
To extract Project-A to NewDev I used the the convert extension, as documented in "split a repository in two". I used a mapfile containing the one mapping include Product-A/Project-A.
So far, NewDev is an exact subtree of OldDev. It does not contain ~/Product-B, it does not contain ~/Product-A/Project-B nor ~/Product-A/Project-C. It only contains ~/Product-A/Project-A. The files that remained are located at exactly the same paths as before, but only those files that belong to Product-A's Project-A were retained.
So, I've achieved half of my goals: I split OldDev, with its many products and projects, and created NewDev with only one project (Project-A). However, the files of Project-A are not at # but at their old (OldDev) location #/Product-A/Project-A. I need to move them up two steps so xxx.cs, will be at #/xxx.cs and not at #/Product-A/Project-A/xxx.cs
To move the files I tried to use the hg mv command, but I can't figure how to specify the root (#) as the destination.
Solution: What worked for me, based on Marc Anton Dahmen's answer, is as follows:
convert1.txt: hg convert -s hg -d hg --filemap mapfile1.txt olddev temprepo
mapfile1.txt: include Product-A/Project-A
convert2.txt: hg convert -s hg -d hg --filemap mapfile2.txt temprepo newrepo
mapfile2.txt: rename Product-A/Project-A .
Where the text of convrert1.txt and convert2.txt, of course, shell commands.
You must use the rename directive in your filemap instead of include like so:
rename Project-A .
Moving every file in a repository and the repository data is not an hg mv operation because that cannot change where the repository meta-data is stored.
The wording of your question is still really ambiguous, but I have a decent guess as to what you want to do.
Suppose you have a repo called /some/dir/avi-repo and you really want it to be in /avi-repo. Use clone:
cd /
hg clone /some /avi-repo
Now you have two identical copies of the repo, one in /some/dir/avi-repo and one in /avi-repo. You can delete all of /some/dir/avi-repo now.
Your desire seems a little more complicated than that with a tree like:
/some
---- /.hg # the repository meta-data
---- /dir # no files in here just the sub-dir
-------- /avi-repo
------------/file.c
------------/file.dat
------------/important-file.txt
And you want to move avi-repo to /some/avi-repo. You should be able to do that with the right sequence of mercurial commands, but it is far easier to:
mkdir /temp
cd /temp
hg clone /some /temp/avi-clone
rm -r /some
mkdir /some
hg clone /temp/avi-clone /some
Or some variant of that. The point is that repatriating an entire repository is not a job for hg mv.
I want to split up one of my Mercurial repositories into two separate repositories. In particular, I want to take a directory sitting at the root level of the repository and convert that into it's own repository.
My initial thought on how to do this is to tell the existing repository to forget about that particular directory, add the directory to .hgignore, and then convert the directory into it's own repository. However, I'd like to preserve the history of the directory in the new repository.
How can I achieve this?
Convert extension with --filemap option will do it. See Converting from Mercurial part
It's also useful to filter Mercurial repositories to get subsets of an
existing one. For example to transform a subdirectory subfoo of a
repository foo into a repository with its own life (while keeping its
full history), do the following:
$ echo include subfoo > /tmp/myfilemap
$ echo rename subfoo . >> /tmp/myfilemap
$ hg convert --filemap /tmp/myfilemap /path/to/repo/foo /tmp/mysubfoo-repo
How can I alter r0 so it looks like I added .hgignore when I created my repository or insert a commit before my current r0?
I just converted a huge SVN repo using hgsvn to mercurial. It took several hours and had to go through about a dozen branches to get the whole thing. My problem now is that .hgignore isn't committed, so when I hgimportsvn a branch, the .hgignore doesn't seem to come with it. I would like to insert that file as part of r0 or insert it before that (and shift everything by 1). I've also tried committing it at the end of my Mercurial trunk checkout, but it seems hgimportsvn always clones (branches?) from the same Mercurial revision my SVN branch was created from so .hgignore is lost again.
You probably need something like the ConvertExtension. Check out the --splicemap option.
To create a new history with a .hgignore file added as the first revision:
Create a new repository whose only revision is the .hgignore commit.
Create a splicemap file containing two 40-char hashes: rev 0 of your current database, and rev 0 of your new database.
Run hg convert <current_db_dir> <new_db_dir> --splicemap splice_filename
This adds each revision in the current database to the new database. The splicemap specifies editing of parents, so if revision 0 of the current database gets its parent set to revision 0 of the new database.
Below is a Windows batch file that creates a 3-revision database and a 1-revision database with an .hgignore file in it, the splices them together. The result should be what you are looking for. If you have a large original database it could take a while, since the entire history of the source database is re-written to the destination database.
#echo off
#REM Create a 3-revision database
hg init current
cd current
echo >file1
hg add
hg ci -m file1
echo >file2
hg add
hg ci -m file2
echo >file3
hg add
hg ci -m file3
#REM Add the first revision to the splice map
hg log -r 0 --template "{node} " > ..\map
#REM Display the result
hg log
cd ..
#REM Create a 1-revision database
hg init ignore
cd ignore
echo glob:*.txt>.hgignore
hg add
hg ci -m ignore
#REM Specify this node as the parent of the other
#REM database's first revision in the splice map
hg log -r 0 --template "{node}\n" >> ..\map
hg log
cd ..
#REM Here's the resulting splice map
type map
#REM Make a copy to store the result
hg clone ignore result
#REM Add revisions from "current" to "result" honoring
#REM the splice map
hg convert current result --splicemap map
#REM Display the result
cd result
hg log
Maybe you could commit .hgignore and then rebase the commit to the beginning of history (see https://www.mercurial-scm.org/wiki/RebaseProject).
I have a repo located at x:/projects/repo1. The working directory has been emptied using hg update null. I want to extract the latest version of some files from there to a local directory.
I tried this:
x:\projects\repo1> hg cat -o c:\sql\%s scripts\*.sql -r tip
I get this error:
scripts\*.sql: No such file in rev 14f07c26178b
The same command works fine if the working directory is not empty. Is there a good reason why this does not work? Or do you know another way of extract some files from there to a local directory?
The hg cat command is for single files. If you want multiple files use the hg archive command, which makes zipfiles or directories full of files. Here's your command:
x:\projects\repo1> hg archive --include scripts\*.sql -r tip c:\sql
It seems that hg cat doesn't support wildcard symbols in paths. So you should use the full file name:
hg cat -r tip scripts/foo.sql
When your working copy is up to date with the tip revision, your shell does wildcard substitution for you.
The hg manifest command also might be helpful for getting tracked file listings.
This answer is to your comment on Andrey's answer:
hg manifest takes a --rev argument that you can use to see the list of all files in your repository:
hg manifest --rev tip
To get the list of files matching a pattern at the tip, use:
hg stat --all --include *.sql --rev tip --no-status
hg stat -A -I *.sql --rev tip -n # using abbreviations.
From there you could redirect the output to a file and edit each line into a hg cat command like in your original question. It appears (to me, at least, having done some experimentation) that hg cat uses the contents of the working directory -- rather than the contents of the repository at the revision specified -- for glob-matching, so once you know the exact name of the file, you can hg cat it at any revision.