bos@559: <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> bos@559: bos@686: <appendix id="svn"> bos@687: <?dbhtml filename="migrating-to-mercurial.html"?> bos@686: <title>Migrating to Mercurial</title> bos@686: bos@691: <para id="x_6e1">A common way to test the waters with a new revision control bos@686: tool is to experiment with switching an existing project, rather bos@686: than starting a new project from scratch.</para> bos@686: bos@691: <para id="x_6e2">In this appendix, we discuss how to import a project's history bos@686: into Mercurial, and what to look out for if you are used to a bos@686: different revision control system.</para> bos@686: bos@686: <sect1> bos@686: <title>Importing history from another system</title> bos@686: bos@691: <para id="x_6e3">Mercurial ships with an extension named bos@686: <literal>convert</literal>, which can import project history bos@686: from most popular revision control systems. At the time this bos@686: book was written, it could import history from the following bos@686: systems:</para> bos@686: <itemizedlist> bos@686: <listitem> bos@691: <para id="x_6e4">Subversion</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6e5">CVS</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6e6">git</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6e7">Darcs</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6e8">Bazaar</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6e9">Monotone</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6ea">GNU Arch</para> bos@691: </listitem> bos@691: <listitem> bos@691: <para id="x_6eb">Mercurial</para> bos@686: </listitem> bos@686: </itemizedlist> bos@686: bos@691: <para id="x_6ec">(To see why Mercurial itself is supported as a source, see bos@686: <xref linkend="svn.filemap"/>.)</para> bos@686: bos@691: <para id="x_6ed">You can enable the extension in the usual way, by editing bos@686: your <filename>~/.hgrc</filename> file.</para> bos@686: bos@686: <programlisting>[extensions] bos@686: convert =</programlisting> bos@686: bos@691: <para id="x_6ee">This will make a <command>hg convert</command> command bos@686: available. The command is easy to use. For instance, this bos@686: command will import the Subversion history for the Nose unit bos@686: testing framework into Mercurial.</para> bos@686: bos@686: <screen><prompt>$</prompt> <userinput>hg convert http://python-nose.googlecode.com/svn/trunk</userinput></screen> bos@686: bos@691: <para id="x_6ef">The <literal>convert</literal> extension operates bos@686: incrementally. In other words, after you have run <command>hg bos@686: convert</command> once, running it again will import any new bos@686: revisions committed after the first run began. Incremental bos@686: conversion will only work if you run <command>hg bos@686: convert</command> in the same Mercurial repository that you bos@686: originally used, because the <literal>convert</literal> bos@686: extension saves some private metadata in a bos@686: non-revision-controlled file named bos@686: <filename>.hg/shamap</filename> inside the target bos@686: repository.</para> bos@686: bos@693: <para>When you want to start making changes using Mercurial, it's bos@693: best to clone the tree in which you are doing your conversions, bos@693: and leave the original tree for future incremental conversions. bos@693: This is the safest way to let you pull and merge future commits bos@693: from the source revision control system into your newly active bos@693: Mercurial project.</para> bos@693: bos@693: <sect2> bos@693: <title>Converting multiple branches</title> bos@693: bos@693: <para>The <command>hg convert</command> command given above bos@693: converts only the history of the <literal>trunk</literal> bos@693: branch of the Subversion repository. If we instead use the bos@693: URL <literal>http://python-nose.googlecode.com/svn</literal>, bos@693: Mercurial will automatically detect the bos@693: <literal>trunk</literal>, <literal>tags</literal> and bos@693: <literal>branches</literal> layout that Subversion projects bos@693: usually use, and it will import each as a separate Mercurial bos@693: branch.</para> bos@693: bos@693: <para>By default, each Subversion branch imported into Mercurial bos@693: is given a branch name. After the conversion completes, you bos@693: can get a list of the active branch names in the Mercurial bos@693: repository using <command>hg branches -a</command>. If you bos@693: would prefer to import the Subversion branches without names, bos@693: pass the <option>--config bos@693: convert.hg.usebranchnames=false</option> option to bos@693: <command>hg convert</command>.</para> bos@693: bos@693: <para>Once you have converted your tree, if you want to follow bos@693: the usual Mercurial practice of working in a tree that bos@693: contains a single branch, you can clone that single branch bos@693: using <command>hg clone -r mybranchname</command>.</para> bos@693: </sect2> bos@693: bos@686: <sect2> bos@686: <title>Mapping user names</title> bos@686: bos@691: <para id="x_6f0">Some revision control tools save only short usernames with bos@686: commits, and these can be difficult to interpret. The norm bos@686: with Mercurial is to save a committer's name and email bos@686: address, which is much more useful for talking to them after bos@686: the fact.</para> bos@686: bos@691: <para id="x_6f1">If you are converting a tree from a revision control bos@686: system that uses short names, you can map those names to bos@686: longer equivalents by passing a <option>--authors</option> bos@686: option to <command>hg convert</command>. This option accepts bos@686: a file name that should contain entries of the following bos@686: form.</para> bos@686: bos@686: <programlisting>arist = Aristotle <aristotle@phil.example.gr> bos@686: soc = Socrates <socrates@phil.example.gr></programlisting> bos@686: bos@691: <para id="x_6f2">Whenever <literal>convert</literal> encounters a commit bos@686: with the username <literal>arist</literal> in the source bos@686: repository, it will use the name <literal>Aristotle bos@686: <aristotle@phil.example.gr></literal> in the converted bos@686: Mercurial revision. If no match is found for a name, it is bos@686: used verbatim.</para> bos@686: </sect2> bos@686: bos@686: <sect2 id="svn.filemap"> bos@686: <title>Tidying up the tree</title> bos@686: bos@691: <para id="x_6f3">Not all projects have pristine history. There may be a bos@686: directory that should never have been checked in, a file that bos@686: is too big, or a whole hierarchy that needs to be bos@686: refactored.</para> bos@686: bos@691: <para id="x_6f4">The <literal>convert</literal> extension supports the idea bos@686: of a <quote>file map</quote> that can reorganize the files and bos@686: directories in a project as it imports the project's history. bos@686: This is useful not only when importing history from other bos@686: revision control systems, but also to prune or refactor a bos@686: Mercurial tree.</para> bos@686: bos@691: <para id="x_6f5">To specify a file map, use the <option>--filemap</option> bos@686: option and supply a file name. A file map contains lines of the bos@686: following forms.</para> bos@686: bos@686: <programlisting># This is a comment. bos@686: # Empty lines are ignored. bos@686: bos@686: include path/to/file bos@686: bos@686: exclude path/to/file bos@686: bos@686: rename from/some/path to/some/other/place bos@686: </programlisting> bos@686: bos@691: <para id="x_6f6">The <literal>include</literal> directive causes a file, or bos@686: all files under a directory, to be included in the destination bos@686: repository. This also excludes all other files and dirs not bos@686: explicitely included. The <literal>exclude</literal> bos@686: directive causes files or directories to be omitted, and bos@686: others not explicitly mentioned to be included.</para> bos@686: bos@691: <para id="x_6f7">To move a file or directory from one location to another, bos@686: use the <literal>rename</literal> directive. If you need to bos@686: move a file or directory from a subdirectory into the root of bos@686: the repository, use <literal>.</literal> as the second bos@686: argument to the <literal>rename</literal> directive.</para> bos@686: </sect2> bos@693: bos@693: <sect2> bos@693: <title>Improving Subversion conversion performance</title> bos@693: bos@693: <para>You will often need several attempts before you hit the bos@693: perfect combination of user map, file map, and other bos@693: conversion parameters. Converting a Subversion repository bos@693: over an access protocol like <literal>ssh</literal> or bos@693: <literal>http</literal> can proceed thousands of times more bos@693: slowly than Mercurial is capable of actually operating, due to bos@693: network delays. This can make tuning that perfect conversion bos@693: recipe very painful.</para> bos@693: bos@693: <para>The <ulink bos@693: url="http://svn.collab.net/repos/svn/trunk/notes/svnsync.txt"><command>svnsync</command></ulink> bos@693: command can greatly speed up the conversion of a Subversion bos@693: repository. It is a read-only mirroring program for bos@693: Subversion repositories. The idea is that you create a local bos@693: mirror of your Subversion tree, then convert the mirror into a bos@693: Mercurial repository.</para> bos@693: bos@693: <para>Suppose we want to convert the Subversion repository for bos@693: the popular Memcached project into a Mercurial tree. First, bos@693: we create a local Subversion repository.</para> bos@693: bos@693: <screen><prompt>$</prompt> <userinput>svnadmin create memcached-mirror</userinput></screen> bos@693: bos@693: <para>Next, we set up a Subversion hook that bos@693: <command>svnsync</command> needs.</para> bos@693: bos@693: <screen><prompt>$</prompt> <userinput>echo '#!/bin/sh' > memcached-mirror/hooks/pre-revprop-change</userinput> bos@693: <prompt>$</prompt> <userinput>chmod +x memcached-mirror/hooks/pre-revprop-change</userinput></screen> bos@693: bos@693: <para>We then initialize <command>svnsync</command> in this bos@693: repository.</para> bos@693: bos@693: <screen><prompt>$</prompt> <userinput>svnsync --init file://`pwd`/memcached-mirror \ bos@693: http://code.sixapart.com/svn/memcached</userinput></screen> bos@693: bos@693: <para>Our next step is to begin the <command>svnsync</command> bos@693: mirroring process.</para> bos@693: bos@693: <screen><prompt>$</prompt> <userinput>svnsync sync file://`pwd`/memcached-mirror</userinput></screen> bos@693: bos@693: <para>Finally, we import the history of our local Subversion bos@693: mirror into Mercurial.</para> bos@693: bos@693: <screen><prompt>$</prompt> <userinput>hg convert memcached-mirror</userinput></screen> bos@693: bos@693: <para>We can use this process incrementally if the Subversion bos@693: repository is still in use. We run <command>svnsync</command> bos@693: to pull new changes into our mirror, then <command>hg bos@693: convert</command> to import them into our Mercurial bos@693: tree.</para> bos@693: bos@693: <para>There are two advantages to doing a two-stage import with bos@693: <command>svnsync</command>. The first is that it uses more bos@693: efficient Subversion network syncing code than <command>hg bos@693: convert</command>, so it transfers less data over the bos@693: network. The second is that the import from a local bos@693: Subversion tree is so fast that you can tweak your conversion bos@693: setup repeatedly without having to sit through a painfully bos@693: slow network-based conversion process each time.</para> bos@693: </sect2> bos@686: </sect1> bos@686: bos@686: <sect1> bos@686: <title>Migrating from Subversion</title> bos@686: bos@691: <para id="x_6f8">Subversion is currently the most popular open source bos@686: revision control system. Although there are many differences bos@686: between Mercurial and Subversion, making the transition from bos@686: Subversion to Mercurial is not particularly difficult. The two bos@686: have similar command sets and generally uniform bos@686: interfaces.</para> bos@686: bos@686: <sect2> bos@686: <title>Philosophical differences</title> bos@686: bos@691: <para id="x_6f9">The fundamental difference between Subversion and bos@686: Mercurial is of course that Subversion is centralized, while bos@686: Mercurial is distributed. Since Mercurial stores all of a bos@686: project's history on your local drive, it only needs to bos@686: perform a network access when you want to explicitly bos@686: communicate with another repository. In contrast, Subversion bos@686: stores very little information locally, and the client must bos@686: thus contact its server for many common operations.</para> bos@686: bos@691: <para id="x_6fa">Subversion more or less gets away without a well-defined bos@686: notion of a branch: which portion of a server's namespace bos@686: qualifies as a branch is a matter of convention, with the bos@686: software providing no enforcement. Mercurial treats a bos@686: repository as the unit of branch management.</para> bos@686: bos@686: <sect3> bos@686: <title>Scope of commands</title> bos@686: bos@691: <para id="x_6fb">Since Subversion doesn't know what parts of its bos@686: namespace are really branches, it treats most commands as bos@686: requests to operate at and below whatever directory you are bos@686: currently visiting. For instance, if you run <command>svn bos@686: log</command>, you'll get the history of whatever part of bos@686: the tree you're looking at, not the tree as a whole.</para> bos@686: bos@691: <para id="x_6fc">Mercurial's commands behave differently, by defaulting bos@686: to operating over an entire repository. Run <command>hg bos@686: log</command> and it will tell you the history of the bos@686: entire tree, no matter what part of the working directory bos@686: you're visiting at the time. If you want the history of bos@686: just a particular file or directory, simply supply it by bos@686: name, e.g. <command>hg log src</command>.</para> bos@686: bos@691: <para id="x_6fd">From my own experience, this difference in default bos@686: behaviors is probably the most likely to trip you up if you bos@686: have to switch back and forth frequently between the two bos@686: tools.</para> bos@686: </sect3> bos@686: bos@686: <sect3> bos@686: <title>Multi-user operation and safety</title> bos@686: bos@691: <para id="x_6fe">With Subversion, it is normal (though slightly frowned bos@686: upon) for multiple people to collaborate in a single branch. bos@686: If Alice and Bob are working together, and Alice commits bos@686: some changes to their shared branch, Bob must update his bos@686: client's view of the branch before he can commit. Since at bos@686: this time he has no permanent record of the changes he has bos@686: made, he can corrupt or lose his modifications during and bos@686: after his update.</para> bos@686: bos@691: <para id="x_6ff">Mercurial encourages a commit-then-merge model instead. bos@686: Bob commits his changes locally before pulling changes from, bos@686: or pushing them to, the server that he shares with Alice. bos@686: If Alice pushed her changes before Bob tries to push his, he bos@686: will not be able to push his changes until he pulls hers, bos@686: merges with them, and commits the result of the merge. If bos@686: he makes a mistake during the merge, he still has the option bos@686: of reverting to the commit that recorded his changes.</para> bos@686: bos@691: <para id="x_700">It is worth emphasizing that these are the common ways bos@686: of working with these tools. Subversion supports a safer bos@686: work-in-your-own-branch model, but it is cumbersome enough bos@686: in practice to not be widely used. Mercurial can support bos@686: the less safe mode of allowing changes to be pulled in and bos@686: merged on top of uncommitted edits, but this is considered bos@686: highly unusual.</para> bos@686: </sect3> bos@686: bos@686: <sect3> bos@686: <title>Published vs local changes</title> bos@686: bos@691: <para id="x_701">A Subversion <command>svn commit</command> command bos@686: immediately publishes changes to a server, where they can be bos@686: seen by everyone who has read access.</para> bos@686: bos@691: <para id="x_702">With Mercurial, commits are always local, and must be bos@686: published via a <command>hg push</command> command bos@686: afterwards.</para> bos@686: bos@691: <para id="x_703">Each approach has its advantages and disadvantages. The bos@686: Subversion model means that changes are published, and hence bos@686: reviewable and usable, immediately. On the other hand, this bos@686: means that a user must have commit access to a repository in bos@686: order to use the software in a normal way, and commit access bos@686: is not lightly given out by most open source bos@686: projects.</para> bos@686: bos@691: <para id="x_704">The Mercurial approach allows anyone who can clone a bos@686: repository to commit changes without the need for someone bos@686: else's permission, and they can then publish their changes bos@686: and continue to participate however they see fit. The bos@686: distinction between committing and pushing does open up the bos@686: possibility of someone committing changes to their laptop bos@686: and walking away for a few days having forgotten to push bos@686: them, which in rare cases might leave collaborators bos@686: temporarily stuck.</para> bos@686: </sect3> bos@686: </sect2> bos@686: bos@686: <sect2> bos@686: <title>Quick reference</title> bos@686: bos@686: <table> bos@686: <title>Subversion commands and Mercurial equivalents</title> bos@686: <tgroup cols="3"> bos@686: <thead> bos@686: <row> bos@686: <entry>Subversion</entry> bos@686: <entry>Mercurial</entry> bos@686: <entry>Notes</entry> bos@686: </row> bos@686: </thead> bos@686: <tbody> bos@686: <row> bos@686: <entry><command>svn add</command></entry> bos@686: <entry><command>hg add</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn blame</command></entry> bos@686: <entry><command>hg annotate</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn cat</command></entry> bos@686: <entry><command>hg cat</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn checkout</command></entry> bos@686: <entry><command>hg clone</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn cleanup</command></entry> bos@686: <entry>n/a</entry> bos@686: <entry>No cleanup needed</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn commit</command></entry> bos@686: <entry><command>hg commit</command>; <command>hg bos@686: push</command></entry> bos@686: <entry><command>hg push</command> publishes after bos@686: commit</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn copy</command></entry> bos@686: <entry><command>hg clone</command></entry> bos@686: <entry>To create a new branch</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn copy</command></entry> bos@686: <entry><command>hg copy</command></entry> bos@686: <entry>To copy files or directories</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn delete</command> (<command>svn bos@686: remove</command>)</entry> bos@686: <entry><command>hg remove</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn diff</command></entry> bos@686: <entry><command>hg diff</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn export</command></entry> bos@686: <entry><command>hg archive</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn help</command></entry> bos@686: <entry><command>hg help</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn import</command></entry> bos@686: <entry><command>hg addremove</command>; <command>hg bos@686: commit</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn info</command></entry> bos@686: <entry><command>hg parents</command></entry> bos@686: <entry>Shows what revision is checked out</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn info</command></entry> bos@686: <entry><command>hg showconfig bos@686: paths.parent</command></entry> bos@686: <entry>Shows what URL is checked out</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn list</command></entry> bos@686: <entry><command>hg manifest</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn log</command></entry> bos@686: <entry><command>hg log</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn merge</command></entry> bos@686: <entry><command>hg merge</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn mkdir</command></entry> bos@686: <entry>n/a</entry> bos@686: <entry>Mercurial does not track directories</entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn move</command> (<command>svn bos@686: rename</command>)</entry> bos@686: <entry><command>hg rename</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn resolved</command></entry> bos@686: <entry><command>hg resolve -m</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn revert</command></entry> bos@686: <entry><command>hg revert</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn status</command></entry> bos@686: <entry><command>hg status</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: <row> bos@686: <entry><command>svn update</command></entry> bos@686: <entry><command>hg pull -u</command></entry> bos@686: <entry></entry> bos@686: </row> bos@686: </tbody> bos@686: </tgroup> bos@686: </table> bos@686: </sect2> bos@686: </sect1> bos@686: bos@686: <sect1> bos@686: <title>Useful tips for newcomers</title> bos@686: bos@691: <para id="x_705">Under some revision control systems, printing a diff for a bos@686: single committed revision can be painful. For instance, with bos@686: Subversion, to see what changed in revision 104654, you must bos@686: type <command>svn diff -r104653:104654</command>. Mercurial bos@686: eliminates the need to type the revision ID twice in this common bos@686: case. For a plain diff, <command>hg export 104654</command>. For bos@686: a log message followed by a diff, <command>hg log -r104654 bos@686: -p</command>.</para> bos@686: bos@691: <para id="x_706">When you run <command>hg status</command> without any bos@686: arguments, it prints the status of the entire tree, with paths bos@686: relative to the root of the repository. This makes it tricky to bos@686: copy a file name from the output of <command>hg status</command> bos@686: into the command line. If you supply a file or directory name bos@686: to <command>hg status</command>, it will print paths relative to bos@686: your current location instead. So to get tree-wide status from bos@686: <command>hg status</command>, with paths that are relative to bos@686: your current directory and not the root of the repository, feed bos@686: the output of <command>hg root</command> into <command>hg bos@686: status</command>. You can easily do this as follows on a bos@686: Unix-like system:</para> bos@686: bos@686: <screen><prompt>$</prompt> <userinput>hg status `hg root`</userinput></screen> bos@686: </sect1> bos@559: </appendix> bos@559: bos@559: <!-- bos@559: local variables: bos@559: sgml-parent-document: ("00book.xml" "book" "appendix") bos@559: end: bos@559: -->