# HG changeset patch # User Romain PELISSE # Date 1250391481 -7200 # Node ID 6b680d569bb437736da700a1d63b7591369f181c # Parent 1dd00abb3fa9b5e89d28bc0328c8e0e6017b1027 deleting a bunch of files not longer necessary to build the documentation. Adding missing newly files needed to build the documentation diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/00book.tex --- a/fr/00book.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -% The use of oneside here is a temporary hack; \marginpar entries -% don't show up on odd pages of PDF output without it. Sigh. -\documentclass[oneside]{book} -\usepackage{enumerate} -\usepackage{fullpage} -\usepackage{makeidx} -\usepackage{ifpdf} -\usepackage{graphicx} -\usepackage{pslatex} -\usepackage{fancyvrb} -% adding package specific to the french version -\usepackage[french]{babel} -\usepackage[utf8]{inputenc} -% leave hyperref until last -\usepackage[colorlinks=true,bookmarks=true,pdftitle={Distributed - revision control with Mercurial},pdfsubject={Revision - control},pdfkeywords={Mercurial, Revision control, Distributed - revision control},pdfauthor={Bryan O'Sullivan}]{hyperref} - -\include{99defs} - -\title{Gestion de source distribué avec Mercurial} \author{Bryan - O'Sullivan} -\date{Copyright \copyright\ 2006, 2007 Bryan O'Sullivan.\\ - Ce document peut être librement distribué selon les termes et - les conditions décrites dans la version 1.0 de la licence Open Publication. - La licence est en annexe~\ref{cha:opl} de ce document.\\ - - Cette traduction a été généré depuis - \href{http://hg.serpentine.com/mercurial/book/}{rev~\input{build_id}} - avec \href{http://www.selenic.com/hg/}{rev~\input{hg_id}} of Mercurial.} - -\makeindex - -\begin{document} - -\maketitle - -\addcontentsline{toc}{chapter}{Contents} -\pagenumbering{roman} -\tableofcontents -\listoffigures -%\listoftables - -\pagenumbering{arabic} - -\include{preface} -\include{intro} -\include{tour-basic} -\include{tour-merge} -\include{concepts} -\include{daily} -\include{collab} -\include{filenames} -\include{branch} -\include{undo} -\include{hook} -\include{template} -\include{mq} -\include{mq-collab} -\include{hgext} - -\appendix -\include{cmdref} -\include{mq-ref} -\include{srcinstall} -\include{license} -\addcontentsline{toc}{chapter}{Bibliography} -\bibliographystyle{alpha} -\bibliography{99book} - -\addcontentsline{toc}{chapter}{Index} -\printindex - -\end{document} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: t -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/00book.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/00book.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%SHORTCUTS; + + + + +%AUTOSNIPPETS; +]> + + + Mercurial: The Definitive Guide + + + Compiled from $rev_id$ + + 1 + 9780596800673 + + + Bryan + O'Sullivan + + + + + Mike + Loukides + + + + 2006 + 2007 + 2008 + 2009 + Bryan O'Sullivan + + + + + &ch00; + + &ch01; + + &ch02; + + &ch03; + + &ch04; + + &ch05; + + &ch06; + + &ch07; + + &ch08; + + &ch09; + + &ch10; + + &ch11; + + &ch12; + + &ch13; + + &ch14; + + &appA; + + &appB; + + &appC; + + &appD; + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/99book.bib --- a/fr/99book.bib Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -@Unpublished{gruenbacher:2005, - author = {Andreas Gruenbacher}, - title = {How To Survive With Many Patches (Introduction to \texttt{quilt})}, - year = {2005}, - month = {June}, - note = {\url{http://www.suse.de/~agruen/quilt.pdf}}, -} - -@InProceedings{web:europython, - author = {Bryan O'Sullivan}, - title = {Achieving High Performance in Mercurial}, - booktitle = {EuroPython Conference}, - year = {2006}, - month = {July}, - note = {\url{XXX}}, -} - -@Misc{web:diffstat, - author = {Thomas Dickey}, - title = {\texttt{diffstat}--make a histogram of \texttt{diff} output}, - note = {\url{http://dickey.his.com/diffstat/diffstat.html}}, -} - -@Misc{web:quilt, - author = {Andreas Gruenbacher, Martin Quinson, Jean Delvare}, - title = {Patchwork Quilt}, - note = {\url{http://savannah.nongnu.org/projects/quilt}}, -} - -@Misc{web:patchutils, - author = {Tim Waugh}, - title = {\texttt{patchutils}--programs that operate on patch files}, - note = {\url{http://cyberelk.net/tim/patchutils/}}, -} - -@Misc{web:mpatch, - author = {Chris Mason}, - title = {\texttt{mpatch}--help solve patch rejects}, - note = {\url{http://oss.oracle.com/~mason/mpatch/}}, -} - -@Misc{web:wiggle, - author = {Neil Brown}, - title = {\texttt{wiggle}--apply conflicting patches}, - note = {\url{http://cgi.cse.unsw.edu.au/~neilb/source/wiggle/}}, -} - -@Misc{web:mysql-python, - author = {Andy Dustman}, - title = {MySQL for Python}, - note = {\url{http://sourceforge.net/projects/mysql-python}}, -} - -@Misc{web:changelog, - author = {Richard Stallman, GNU Project volunteers}, - title = {GNU Coding Standards---Change Logs}, - note = {\url{http://www.gnu.org/prep/standards/html_node/Change-Logs.html}}, -} - -@Misc{web:macpython, - author = {Bob Ippolito, Ronald Oussoren}, - title = {Universal MacPython}, - note = {\url{http://bob.pythonmac.org/archives/2006/04/10/python-and-universal-binaries-on-mac-os-x/}}, -} - -@Misc{web:putty, - author = {Simon Tatham}, - title = {PuTTY---open source ssh client for Windows}, - note = {\url{http://www.chiark.greenend.org.uk/~sgtatham/putty/}}, -} - -@Misc{web:configparser, - author = {Python.org}, - title = {\texttt{ConfigParser}---Configuration file parser}, - note = {\url{http://docs.python.org/lib/module-ConfigParser.html}}, -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/99defs.tex --- a/fr/99defs.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -% Bug ID. -\newcommand{\bug}[1]{\index{Mercurial bug - database!\href{http://www.selenic.com/mercurial/bts/issue#1}{bug - ~#1}}\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial - bug no.~#1}} - -% File name in the user's home directory. -\newcommand{\tildefile}[1]{\texttt{\~{}/#1}} - -% File name. -\newcommand{\filename}[1]{\texttt{#1}} - -% Directory name. -\newcommand{\dirname}[1]{\texttt{#1}} - -% File name, with index entry. -% The ``s'' prefix comes from ``special''. -\newcommand{\sfilename}[1]{\index{\texttt{#1} file}\texttt{#1}} - -% Directory name, with index entry. -\newcommand{\sdirname}[1]{\index{\texttt{#1} directory}\texttt{#1}} - -% Mercurial extension. -\newcommand{\hgext}[1]{\index{\texttt{#1} extension}\texttt{#1}} - -% Command provided by a Mercurial extension. -\newcommand{\hgxcmd}[2]{\index{\texttt{#2} command (\texttt{#1} - extension)}\index{\texttt{#1} extension!\texttt{#2} command}``\texttt{hg #2}''} - -% Mercurial command. -\newcommand{\hgcmd}[1]{\index{\texttt{#1} command}``\texttt{hg #1}''} - -% Mercurial command, with arguments. -\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''} - -\newcommand{\tplkword}[1]{\index{\texttt{#1} template keyword}\index{template keywords!\texttt{#1}}\texttt{#1}} - -\newcommand{\tplkwfilt}[2]{\index{\texttt{#1} template keyword!\texttt{#2} - filter}\index{template filters!\texttt{#2}}\index{\texttt{#2} - template filter}\texttt{#2}} - -\newcommand{\tplfilter}[1]{\index{template - filters!\texttt{#1}}\index{\texttt{#1} template - filter}\texttt{#1}} - -% Shell/system command. -\newcommand{\command}[1]{\index{\texttt{#1} system command}\texttt{#1}} - -% Shell/system command, with arguments. -\newcommand{\cmdargs}[2]{\index{\texttt{#1} system command}``\texttt{#1 #2}''} - -% Mercurial command option. -\newcommand{\hgopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}} - -% Mercurial command option, provided by an extension command. -\newcommand{\hgxopt}[3]{\index{\texttt{#2} command (\texttt{#1} extension)!\texttt{#3} option}\index{\texttt{#1} extension!\texttt{#2} command!\texttt{#3} option}\texttt{#3}} - -% Mercurial global option. -\newcommand{\hggopt}[1]{\index{global options!\texttt{#1} option}\texttt{#1}} - -% Shell/system command option. -\newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}} - -% Command option. -\newcommand{\option}[1]{\texttt{#1}} - -% Software package. -\newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}} - -% Section name from a hgrc file. -\newcommand{\rcsection}[1]{\index{\texttt{hgrc} file!\texttt{#1} section}\texttt{[#1]}} - -% Named item in a hgrc file section. -\newcommand{\rcitem}[2]{\index{\texttt{hgrc} file!\texttt{#1} - section!\texttt{#2} entry}\texttt{#2}} - -% hgrc file. -\newcommand{\hgrc}{\index{configuration file!\texttt{hgrc} - (Linux/Unix)}\index{\texttt{hgrc} configuration file}\texttt{hgrc}} - -% Mercurial.ini file. -\newcommand{\hgini}{\index{configuration file!\texttt{Mercurial.ini} - (Windows)}\index{\texttt{Mercurial.ini} configuration file}\texttt{Mercurial.ini}} - -% Hook name. -\newcommand{\hook}[1]{\index{\texttt{#1} hook}\index{hooks!\texttt{#1}}\texttt{#1}} - -% Environment variable. -\newcommand{\envar}[1]{\index{\texttt{#1} environment - variable}\index{environment variables!\texttt{#1}}\texttt{#1}} - -% Python module. -\newcommand{\pymod}[1]{\index{\texttt{#1} module}\texttt{#1}} - -% Python class in a module. -\newcommand{\pymodclass}[2]{\index{\texttt{#1} module!\texttt{#2} - class}\texttt{#1.#2}} - -% Python function in a module. -\newcommand{\pymodfunc}[2]{\index{\texttt{#1} module!\texttt{#2} - function}\texttt{#1.#2}} - -% Note: blah blah. -\newsavebox{\notebox} -\newenvironment{note}% - {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Note:}\space}% - {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}} -\newenvironment{caution}% - {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Caution:}\space}% - {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}} - -% Code sample, eating 4 characters of leading space. -\DefineVerbatimEnvironment{codesample4}{Verbatim}{frame=single,gobble=4,numbers=left,commandchars=\\\{\}} - -% Code sample, eating 2 characters of leading space. -\DefineVerbatimEnvironment{codesample2}{Verbatim}{frame=single,gobble=2,numbers=left,commandchars=\\\{\}} - -% Interaction from the examples directory. -\newcommand{\interaction}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{examples/#1.lxo}} -% Example code from the examples directory. -\newcommand{\excode}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{../examples/#1}} - -% Graphics inclusion. -\ifpdf - \newcommand{\grafix}[1]{\includegraphics{#1}} -\else - \newcommand{\grafix}[1]{\includegraphics{#1.png}} -\fi - -% Reference entry for a command. -\newcommand{\cmdref}[2]{\section{\hgcmd{#1}---#2}\label{cmdref:#1}\index{\texttt{#1} command}} - -% Reference entry for a command option with long and short forms. -\newcommand{\optref}[3]{\subsubsection{\hgopt{#1}{--#3}, also \hgopt{#1}{-#2}}} - -% Reference entry for a command option with only long form. -\newcommand{\loptref}[2]{\subsubsection{\hgopt{#1}{--#2} option}} - -% command to generate a footnote to be used as a translator's note -\newcommand{\ndt}[1]{\footnote{\textbf{N. del T.} #1}} - - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/appA-svn.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/appA-svn.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,540 @@ + + + + +Migrating to Mercurial + + A common way to test the waters with a new revision control + tool is to experiment with switching an existing project, rather + than starting a new project from scratch. + + In this appendix, we discuss how to import a project's history + into Mercurial, and what to look out for if you are used to a + different revision control system. + + + Importing history from another system + + Mercurial ships with an extension named + convert, which can import project history + from most popular revision control systems. At the time this + book was written, it could import history from the following + systems: + + + Subversion + + + CVS + + + git + + + Darcs + + + Bazaar + + + Monotone + + + GNU Arch + + + Mercurial + + + + (To see why Mercurial itself is supported as a source, see + .) + + You can enable the extension in the usual way, by editing + your ~/.hgrc file. + + [extensions] +convert = + + This will make a hg convert command + available. The command is easy to use. For instance, this + command will import the Subversion history for the Nose unit + testing framework into Mercurial. + + $ hg convert http://python-nose.googlecode.com/svn/trunk + + The convert extension operates + incrementally. In other words, after you have run hg + convert once, running it again will import any new + revisions committed after the first run began. Incremental + conversion will only work if you run hg + convert in the same Mercurial repository that you + originally used, because the convert + extension saves some private metadata in a + non-revision-controlled file named + .hg/shamap inside the target + repository. + + When you want to start making changes using Mercurial, it's + best to clone the tree in which you are doing your conversions, + and leave the original tree for future incremental conversions. + This is the safest way to let you pull and merge future commits + from the source revision control system into your newly active + Mercurial project. + + + Converting multiple branches + + The hg convert command given above + converts only the history of the trunk + branch of the Subversion repository. If we instead use the + URL http://python-nose.googlecode.com/svn, + Mercurial will automatically detect the + trunk, tags and + branches layout that Subversion projects + usually use, and it will import each as a separate Mercurial + branch. + + By default, each Subversion branch imported into Mercurial + is given a branch name. After the conversion completes, you + can get a list of the active branch names in the Mercurial + repository using hg branches -a. If you + would prefer to import the Subversion branches without names, + pass the option to + hg convert. + + Once you have converted your tree, if you want to follow + the usual Mercurial practice of working in a tree that + contains a single branch, you can clone that single branch + using hg clone -r mybranchname. + + + + Mapping user names + + Some revision control tools save only short usernames with + commits, and these can be difficult to interpret. The norm + with Mercurial is to save a committer's name and email + address, which is much more useful for talking to them after + the fact. + + If you are converting a tree from a revision control + system that uses short names, you can map those names to + longer equivalents by passing a + option to hg convert. This option accepts + a file name that should contain entries of the following + form. + + arist = Aristotle <aristotle@phil.example.gr> +soc = Socrates <socrates@phil.example.gr> + + Whenever convert encounters a commit + with the username arist in the source + repository, it will use the name Aristotle + <aristotle@phil.example.gr> in the converted + Mercurial revision. If no match is found for a name, it is + used verbatim. + + + + Tidying up the tree + + Not all projects have pristine history. There may be a + directory that should never have been checked in, a file that + is too big, or a whole hierarchy that needs to be + refactored. + + The convert extension supports the idea + of a file map that can reorganize the files and + directories in a project as it imports the project's history. + This is useful not only when importing history from other + revision control systems, but also to prune or refactor a + Mercurial tree. + + To specify a file map, use the + option and supply a file name. A file map contains lines of the + following forms. + + # This is a comment. +# Empty lines are ignored. + +include path/to/file + +exclude path/to/file + +rename from/some/path to/some/other/place + + + The include directive causes a file, or + all files under a directory, to be included in the destination + repository. This also excludes all other files and dirs not + explicitely included. The exclude + directive causes files or directories to be omitted, and + others not explicitly mentioned to be included. + + To move a file or directory from one location to another, + use the rename directive. If you need to + move a file or directory from a subdirectory into the root of + the repository, use . as the second + argument to the rename directive. + + + + Improving Subversion conversion performance + + You will often need several attempts before you hit the + perfect combination of user map, file map, and other + conversion parameters. Converting a Subversion repository + over an access protocol like ssh or + http can proceed thousands of times more + slowly than Mercurial is capable of actually operating, due to + network delays. This can make tuning that perfect conversion + recipe very painful. + + The svnsync + command can greatly speed up the conversion of a Subversion + repository. It is a read-only mirroring program for + Subversion repositories. The idea is that you create a local + mirror of your Subversion tree, then convert the mirror into a + Mercurial repository. + + Suppose we want to convert the Subversion repository for + the popular Memcached project into a Mercurial tree. First, + we create a local Subversion repository. + + $ svnadmin create memcached-mirror + + Next, we set up a Subversion hook that + svnsync needs. + + $ echo '#!/bin/sh' > memcached-mirror/hooks/pre-revprop-change +$ chmod +x memcached-mirror/hooks/pre-revprop-change + + We then initialize svnsync in this + repository. + + $ svnsync --init file://`pwd`/memcached-mirror \ + http://code.sixapart.com/svn/memcached + + Our next step is to begin the svnsync + mirroring process. + + $ svnsync sync file://`pwd`/memcached-mirror + + Finally, we import the history of our local Subversion + mirror into Mercurial. + + $ hg convert memcached-mirror + + We can use this process incrementally if the Subversion + repository is still in use. We run svnsync + to pull new changes into our mirror, then hg + convert to import them into our Mercurial + tree. + + There are two advantages to doing a two-stage import with + svnsync. The first is that it uses more + efficient Subversion network syncing code than hg + convert, so it transfers less data over the + network. The second is that the import from a local + Subversion tree is so fast that you can tweak your conversion + setup repeatedly without having to sit through a painfully + slow network-based conversion process each time. + + + + + Migrating from Subversion + + Subversion is currently the most popular open source + revision control system. Although there are many differences + between Mercurial and Subversion, making the transition from + Subversion to Mercurial is not particularly difficult. The two + have similar command sets and generally uniform + interfaces. + + + Philosophical differences + + The fundamental difference between Subversion and + Mercurial is of course that Subversion is centralized, while + Mercurial is distributed. Since Mercurial stores all of a + project's history on your local drive, it only needs to + perform a network access when you want to explicitly + communicate with another repository. In contrast, Subversion + stores very little information locally, and the client must + thus contact its server for many common operations. + + Subversion more or less gets away without a well-defined + notion of a branch: which portion of a server's namespace + qualifies as a branch is a matter of convention, with the + software providing no enforcement. Mercurial treats a + repository as the unit of branch management. + + + Scope of commands + + Since Subversion doesn't know what parts of its + namespace are really branches, it treats most commands as + requests to operate at and below whatever directory you are + currently visiting. For instance, if you run svn + log, you'll get the history of whatever part of + the tree you're looking at, not the tree as a whole. + + Mercurial's commands behave differently, by defaulting + to operating over an entire repository. Run hg + log and it will tell you the history of the + entire tree, no matter what part of the working directory + you're visiting at the time. If you want the history of + just a particular file or directory, simply supply it by + name, e.g. hg log src. + + From my own experience, this difference in default + behaviors is probably the most likely to trip you up if you + have to switch back and forth frequently between the two + tools. + + + + Multi-user operation and safety + + With Subversion, it is normal (though slightly frowned + upon) for multiple people to collaborate in a single branch. + If Alice and Bob are working together, and Alice commits + some changes to their shared branch, Bob must update his + client's view of the branch before he can commit. Since at + this time he has no permanent record of the changes he has + made, he can corrupt or lose his modifications during and + after his update. + + Mercurial encourages a commit-then-merge model instead. + Bob commits his changes locally before pulling changes from, + or pushing them to, the server that he shares with Alice. + If Alice pushed her changes before Bob tries to push his, he + will not be able to push his changes until he pulls hers, + merges with them, and commits the result of the merge. If + he makes a mistake during the merge, he still has the option + of reverting to the commit that recorded his changes. + + It is worth emphasizing that these are the common ways + of working with these tools. Subversion supports a safer + work-in-your-own-branch model, but it is cumbersome enough + in practice to not be widely used. Mercurial can support + the less safe mode of allowing changes to be pulled in and + merged on top of uncommitted edits, but this is considered + highly unusual. + + + + Published vs local changes + + A Subversion svn commit command + immediately publishes changes to a server, where they can be + seen by everyone who has read access. + + With Mercurial, commits are always local, and must be + published via a hg push command + afterwards. + + Each approach has its advantages and disadvantages. The + Subversion model means that changes are published, and hence + reviewable and usable, immediately. On the other hand, this + means that a user must have commit access to a repository in + order to use the software in a normal way, and commit access + is not lightly given out by most open source + projects. + + The Mercurial approach allows anyone who can clone a + repository to commit changes without the need for someone + else's permission, and they can then publish their changes + and continue to participate however they see fit. The + distinction between committing and pushing does open up the + possibility of someone committing changes to their laptop + and walking away for a few days having forgotten to push + them, which in rare cases might leave collaborators + temporarily stuck. + + + + + Quick reference + + + Subversion commands and Mercurial equivalents + + + + Subversion + Mercurial + Notes + + + + + svn add + hg add + + + + svn blame + hg annotate + + + + svn cat + hg cat + + + + svn checkout + hg clone + + + + svn cleanup + n/a + No cleanup needed + + + svn commit + hg commit; hg + push + hg push publishes after + commit + + + svn copy + hg clone + To create a new branch + + + svn copy + hg copy + To copy files or directories + + + svn delete (svn + remove) + hg remove + + + + svn diff + hg diff + + + + svn export + hg archive + + + + svn help + hg help + + + + svn import + hg addremove; hg + commit + + + + svn info + hg parents + Shows what revision is checked out + + + svn info + hg showconfig + paths.parent + Shows what URL is checked out + + + svn list + hg manifest + + + + svn log + hg log + + + + svn merge + hg merge + + + + svn mkdir + n/a + Mercurial does not track directories + + + svn move (svn + rename) + hg rename + + + + svn resolved + hg resolve -m + + + + svn revert + hg revert + + + + svn status + hg status + + + + svn update + hg pull -u + + + + +
+
+
+ + + Useful tips for newcomers + + Under some revision control systems, printing a diff for a + single committed revision can be painful. For instance, with + Subversion, to see what changed in revision 104654, you must + type svn diff -r104653:104654. Mercurial + eliminates the need to type the revision ID twice in this common + case. For a plain diff, hg export 104654. For + a log message followed by a diff, hg log -r104654 + -p. + + When you run hg status without any + arguments, it prints the status of the entire tree, with paths + relative to the root of the repository. This makes it tricky to + copy a file name from the output of hg status + into the command line. If you supply a file or directory name + to hg status, it will print paths relative to + your current location instead. So to get tree-wide status from + hg status, with paths that are relative to + your current directory and not the root of the repository, feed + the output of hg root into hg + status. You can easily do this as follows on a + Unix-like system: + + $ hg status `hg root` + +
+ + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/appB-mq-ref.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/appB-mq-ref.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,563 @@ + + + + + Mercurial Queues reference + + + MQ command reference + + For an overview of the commands provided by MQ, use the + command hg help mq. + + + <command role="hg-ext-mq">qapplied</command>&emdash;print + applied patches + + The qapplied command + prints the current stack of applied patches. Patches are + printed in oldest-to-newest order, so the last patch in the + list is the top patch. + + + + <command role="hg-ext-mq">qcommit</command>&emdash;commit + changes in the queue repository + + The qcommit command + commits any outstanding changes in the .hg/patches + repository. This command only works if the .hg/patches + directory is a repository, i.e. you created the directory + using hg qinit or + ran hg init in the directory + after running qinit. + + This command is shorthand for hg + commit --cwd .hg/patches. + + + <command + role="hg-ext-mq">qdelete</command>&emdash;delete a patch + from the <filename role="special">series</filename> + file + + The qdelete command + removes the entry for a patch from the series file in the .hg/patches + directory. It does not pop the patch if the patch is already + applied. By default, it does not delete the patch file; use + the option + to do that. + + Options: + + : Delete the + patch file. + + + + + <command role="hg-ext-mq">qdiff</command>&emdash;print a + diff of the topmost applied patch + + The qdiff command + prints a diff of the topmost applied patch. It is equivalent + to hg diff -r-2:-1. + + + + <command role="hg-ext-mq">qfold</command>&emdash;move + applied patches into repository history + + The hg qfinish command converts the + specified applied patches into permanent changes by moving + them out of MQ's control so that they will be treated as + normal repository history. + + + + <command role="hg-ext-mq">qfold</command>&emdash;merge + (<quote>fold</quote>) several patches into one + + The qfold command + merges multiple patches into the topmost applied patch, so + that the topmost applied patch makes the union of all of the + changes in the patches in question. + + The patches to fold must not be applied; qfold will exit with an error if + any is. The order in which patches are folded is significant; + hg qfold a b means + apply the current topmost patch, followed by + a, followed by + b. + + The comments from the folded patches are appended to the + comments of the destination patch, with each block of comments + separated by three asterisk + (*) characters. Use the + option to + edit the commit message for the combined patch/changeset after + the folding has completed. + + Options: + + : Edit the + commit message and patch description for the newly folded + patch. + + : Use the + contents of the given file as the new commit message and + patch description for the folded patch. + + : Use the + given text as the new commit message and patch description + for the folded patch. + + + + + <command + role="hg-ext-mq">qheader</command>&emdash;display the + header/description of a patch + + The qheader command + prints the header, or description, of a patch. By default, it + prints the header of the topmost applied patch. Given an + argument, it prints the header of the named patch. + + + + <command role="hg-ext-mq">qimport</command>&emdash;import + a third-party patch into the queue + + The qimport command + adds an entry for an external patch to the series file, and copies the patch + into the .hg/patches directory. It adds + the entry immediately after the topmost applied patch, but + does not push the patch. + + If the .hg/patches directory is a + repository, qimport + automatically does an hg add + of the imported patch. + + + + <command role="hg-ext-mq">qinit</command>&emdash;prepare + a repository to work with MQ + + The qinit command + prepares a repository to work with MQ. It creates a directory + called .hg/patches. + + Options: + + : Create + .hg/patches as a repository + in its own right. Also creates a .hgignore file that will + ignore the status + file. + + + When the .hg/patches directory is a + repository, the qimport + and qnew commands + automatically hg add new + patches. + + + + <command role="hg-ext-mq">qnew</command>&emdash;create a + new patch + + The qnew command + creates a new patch. It takes one mandatory argument, the + name to use for the patch file. The newly created patch is + created empty by default. It is added to the series file after the current + topmost applied patch, and is immediately pushed on top of + that patch. + + If qnew finds modified + files in the working directory, it will refuse to create a new + patch unless the option is used + (see below). This behavior allows you to qrefresh your topmost applied + patch before you apply a new patch on top of it. + + Options: + + : Create a new + patch if the contents of the working directory are + modified. Any outstanding modifications are added to the + newly created patch, so after this command completes, the + working directory will no longer be modified. + + : Use the given + text as the commit message. This text will be stored at + the beginning of the patch file, before the patch + data. + + + + + <command role="hg-ext-mq">qnext</command>&emdash;print + the name of the next patch + + The qnext command + prints the name name of the next patch in the series file after the topmost + applied patch. This patch will become the topmost applied + patch if you run qpush. + + + + <command role="hg-ext-mq">qpop</command>&emdash;pop + patches off the stack + + The qpop command + removes applied patches from the top of the stack of applied + patches. By default, it removes only one patch. + + This command removes the changesets that represent the + popped patches from the repository, and updates the working + directory to undo the effects of the patches. + + This command takes an optional argument, which it uses as + the name or index of the patch to pop to. If given a name, it + will pop patches until the named patch is the topmost applied + patch. If given a number, qpop treats the number as an + index into the entries in the series file, counting from zero + (empty lines and lines containing only comments do not count). + It pops patches until the patch identified by the given index + is the topmost applied patch. + + The qpop command does + not read or write patches or the series file. It is thus safe to + qpop a patch that you have + removed from the series + file, or a patch that you have renamed or deleted entirely. + In the latter two cases, use the name of the patch as it was + when you applied it. + + By default, the qpop + command will not pop any patches if the working directory has + been modified. You can override this behavior using the + option, + which reverts all modifications in the working + directory. + + Options: + + : Pop all + applied patches. This returns the repository to its state + before you applied any patches. + + : Forcibly + revert any modifications to the working directory when + popping. + + : Pop a patch + from the named queue. + + + The qpop command + removes one line from the end of the status file for each patch that it + pops. + + + + <command role="hg-ext-mq">qprev</command>&emdash;print + the name of the previous patch + + The qprev command + prints the name of the patch in the series file that comes before the + topmost applied patch. This will become the topmost applied + patch if you run qpop. + + + + <command role="hg-ext-mq">qpush</command>&emdash;push + patches onto the stack + + The qpush command adds + patches onto the applied stack. By default, it adds only one + patch. + + This command creates a new changeset to represent each + applied patch, and updates the working directory to apply the + effects of the patches. + + The default data used when creating a changeset are as + follows: + + The commit date and time zone are the current + date and time zone. Because these data are used to + compute the identity of a changeset, this means that if + you qpop a patch and + qpush it again, the + changeset that you push will have a different identity + than the changeset you popped. + + The author is the same as the default used by + the hg commit + command. + + The commit message is any text from the patch + file that comes before the first diff header. If there is + no such text, a default commit message is used that + identifies the name of the patch. + + If a patch contains a Mercurial patch header, + the information in the patch header overrides these + defaults. + + Options: + + : Push all + unapplied patches from the series file until there are + none left to push. + + : Add the name + of the patch to the end of the commit message. + + : If a patch + fails to apply cleanly, use the entry for the patch in + another saved queue to compute the parameters for a + three-way merge, and perform a three-way merge using the + normal Mercurial merge machinery. Use the resolution of + the merge as the new patch content. + + : Use the + named queue if merging while pushing. + + + The qpush command + reads, but does not modify, the series file. It appends one line + to the hg status file for + each patch that it pushes. + + + + <command + role="hg-ext-mq">qrefresh</command>&emdash;update the + topmost applied patch + + The qrefresh command + updates the topmost applied patch. It modifies the patch, + removes the old changeset that represented the patch, and + creates a new changeset to represent the modified + patch. + + The qrefresh command + looks for the following modifications: + + Changes to the commit message, i.e. the text + before the first diff header in the patch file, are + reflected in the new changeset that represents the + patch. + + Modifications to tracked files in the working + directory are added to the patch. + + Changes to the files tracked using hg add, hg copy, hg remove, or hg rename. Added files and copy + and rename destinations are added to the patch, while + removed files and rename sources are removed. + + + Even if qrefresh + detects no changes, it still recreates the changeset that + represents the patch. This causes the identity of the + changeset to differ from the previous changeset that + identified the patch. + + Options: + + : Modify + the commit and patch description, using the preferred text + editor. + + : Modify + the commit message and patch description, using the given + text. + + : Modify + the commit message and patch description, using text from + the given file. + + + + + <command role="hg-ext-mq">qrename</command>&emdash;rename + a patch + + The qrename command + renames a patch, and changes the entry for the patch in the + series file. + + With a single argument, qrename renames the topmost + applied patch. With two arguments, it renames its first + argument to its second. + + + + <command role="hg-ext-mq">qseries</command>&emdash;print + the entire patch series + + The qseries command + prints the entire patch series from the series file. It prints only patch + names, not empty lines or comments. It prints in order from + first to be applied to last. + + + + <command role="hg-ext-mq">qtop</command>&emdash;print the + name of the current patch + + The qtop prints the + name of the topmost currently applied patch. + + + + <command + role="hg-ext-mq">qunapplied</command>&emdash;print patches + not yet applied + + The qunapplied command + prints the names of patches from the series file that are not yet + applied. It prints them in order from the next patch that + will be pushed to the last. + + + + <command role="hg-cmd">hg strip</command>&emdash;remove a + revision and descendants + + The hg strip command + removes a revision, and all of its descendants, from the + repository. It undoes the effects of the removed revisions + from the repository, and updates the working directory to the + first parent of the removed revision. + + The hg strip command + saves a backup of the removed changesets in a bundle, so that + they can be reapplied if removed in error. + + Options: + + : Save + unrelated changesets that are intermixed with the stripped + changesets in the backup bundle. + + : If a + branch has multiple heads, remove all heads. + + : Do + not save a backup bundle. + + + + + + MQ file reference + + + The <filename role="special">series</filename> + file + + The series file + contains a list of the names of all patches that MQ can apply. + It is represented as a list of names, with one name saved per + line. Leading and trailing white space in each line are + ignored. + + Lines may contain comments. A comment begins with the + # character, and extends to + the end of the line. Empty lines, and lines that contain only + comments, are ignored. + + You will often need to edit the series file by hand, hence the + support for comments and empty lines noted above. For + example, you can comment out a patch temporarily, and qpush will skip over that patch + when applying patches. You can also change the order in which + patches are applied by reordering their entries in the + series file. + + Placing the series + file under revision control is also supported; it is a good + idea to place all of the patches that it refers to under + revision control, as well. If you create a patch directory + using the + option to qinit, this will + be done for you automatically. + + + + The <filename role="special">status</filename> + file + + The status file + contains the names and changeset hashes of all patches that MQ + currently has applied. Unlike the series file, this file is not + intended for editing. You should not place this file under + revision control, or modify it in any way. It is used by MQ + strictly for internal book-keeping. + + + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/appC-srcinstall.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/appC-srcinstall.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,66 @@ + + + + + Installing Mercurial from source + + + On a Unix-like system + + If you are using a Unix-like system that has a sufficiently + recent version of Python (2.3 or newer) available, it is easy to + install Mercurial from source. + + Download a recent source tarball from http://www.selenic.com/mercurial/download. + + Unpack the tarball: + gzip -dc mercurial-MYVERSION.tar.gz | tar xf - + + Go into the source directory and run the + installer script. This will build Mercurial and install it + in your home directory. + cd mercurial-MYVERSION +python setup.py install --force --home=$HOME + + + Once the install finishes, Mercurial will be in the + bin subdirectory of your home directory. + Don't forget to make sure that this directory is present in your + shell's search path. + + You will probably need to set the PYTHONPATH + environment variable so that the Mercurial executable can find + the rest of the Mercurial packages. For example, on my laptop, + I have set it to /home/bos/lib/python. The + exact path that you will need to use depends on how Python was + built for your system, but should be easy to figure out. If + you're uncertain, look through the output of the installer + script above, and see where the contents of the + mercurial directory were installed to. + + + + On Windows + + Building and installing Mercurial on Windows requires a + variety of tools, a fair amount of technical knowledge, and + considerable patience. I very much do not + recommend this route if you are a casual + user. Unless you intend to hack on Mercurial, I + strongly suggest that you use a binary package instead. + + If you are intent on building Mercurial from source on + Windows, follow the hard way directions on the + Mercurial wiki at http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall, + and expect the process to involve a lot of fiddly work. + + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/appD-license.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/appD-license.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,185 @@ + + + +Open Publication License +\label{cha:opl} + +Version 1.0, 8 June 1999 + + +Requirements on both unmodified and modified versions + +The Open Publication works may be reproduced and distributed in whole +or in part, in any medium physical or electronic, provided that the +terms of this license are adhered to, and that this license or an +incorporation of it by reference (with any options elected by the +author(s) and/or publisher) is displayed in the reproduction. + +Proper form for an incorporation by reference is as follows: + +
+ Copyright (c) year by author's name or designee. This + material may be distributed only subject to the terms and conditions + set forth in the Open Publication License, vx.y or later (the + latest version is presently available at + http://www.opencontent.org/openpub/). +
+ +The reference must be immediately followed with any options elected by +the author(s) and/or publisher of the document (see +section ). + +Commercial redistribution of Open Publication-licensed material is +permitted. + +Any publication in standard (paper) book form shall require the +citation of the original publisher and author. The publisher and +author's names shall appear on all outer surfaces of the book. On all +outer surfaces of the book the original publisher's name shall be as +large as the title of the work and cited as possessive with respect to +the title. + +
+ +Copyright + +The copyright to each Open Publication is owned by its author(s) or +designee. + + + + +Scope of license + +The following license terms apply to all Open Publication works, +unless otherwise explicitly stated in the document. + + +Mere aggregation of Open Publication works or a portion of an Open +Publication work with other works or programs on the same media shall +not cause this license to apply to those other works. The aggregate +work shall contain a notice specifying the inclusion of the Open +Publication material and appropriate copyright notice. + + +Severability. If any part of this license is found to be +unenforceable in any jurisdiction, the remaining portions of the +license remain in force. + + +No warranty. Open Publication works are licensed and provided +as is without warranty of any kind, express or implied, including, +but not limited to, the implied warranties of merchantability and +fitness for a particular purpose or a warranty of non-infringement. + + + + +Requirements on modified works + +All modified versions of documents covered by this license, including +translations, anthologies, compilations and partial documents, must +meet the following requirements: + + + +The modified version must be labeled as such. + + +The person making the modifications must be identified and the + modifications dated. + + +Acknowledgement of the original author and publisher if + applicable must be retained according to normal academic citation + practices. + + +The location of the original unmodified document must be + identified. + + +The original author's (or authors') name(s) may not be used to + assert or imply endorsement of the resulting document without the + original author's (or authors') permission. + + + + + +Good-practice recommendations + +In addition to the requirements of this license, it is requested from +and strongly recommended of redistributors that: + + + +If you are distributing Open Publication works on hardcopy or + CD-ROM, you provide email notification to the authors of your intent + to redistribute at least thirty days before your manuscript or media + freeze, to give the authors time to provide updated documents. This + notification should describe modifications, if any, made to the + document. + + +All substantive modifications (including deletions) be either + clearly marked up in the document or else described in an attachment + to the document. + + +Finally, while it is not mandatory under this license, it is + considered good form to offer a free copy of any hardcopy and CD-ROM + expression of an Open Publication-licensed work to its author(s). + + + + + +License options +\label{sec:opl:options} + + +The author(s) and/or publisher of an Open Publication-licensed +document may elect certain options by appending language to the +reference to or copy of the license. These options are considered part +of the license instance and must be included with the license (or its +incorporation by reference) in derived works. + + + +To prohibit distribution of substantively modified versions + without the explicit permission of the author(s). Substantive + modification is defined as a change to the semantic content of the + document, and excludes mere changes in format or typographical + corrections. + + + To accomplish this, add the phrase Distribution of substantively + modified versions of this document is prohibited without the + explicit permission of the copyright holder. to the license + reference or copy. + + + + +To prohibit any publication of this work or derivative works in + whole or in part in standard (paper) book form for commercial + purposes is prohibited unless prior permission is obtained from the + copyright holder. + + + To accomplish this, add the phrase Distribution of the work or + derivative of the work in any standard (paper) book form is + prohibited unless prior permission is obtained from the copyright + holder. to the license reference or copy. + + + + +
+ + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/autoid.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/autoid.py Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Add unique ID attributes to para tags. This script should only be +# run by one person, since otherwise it introduces the possibility of +# chaotic conflicts among tags. + +import glob, os, re, sys + +tagged = re.compile(']* id="x_([0-9a-f]+)"[^>]*>', re.M) +untagged = re.compile('') + +names = glob.glob('ch*.xml') + glob.glob('app*.xml') + +# First pass: find the highest-numbered paragraph ID. + +biggest_id = 0 +seen = set() +errs = 0 + +for name in names: + for m in tagged.finditer(open(name).read()): + i = int(m.group(1),16) + if i in seen: + print >> sys.stderr, '%s: duplication of ID %s' % (name, i) + errs += 1 + seen.add(i) + if i > biggest_id: + biggest_id = i + +def retag(s): + global biggest_id + biggest_id += 1 + return '' % biggest_id + +# Second pass: add IDs to paragraphs that currently lack them. + +for name in names: + f = open(name).read() + f1 = untagged.sub(retag, f) + if f1 != f: + tmpname = name + '.tmp' + fp = open(tmpname, 'w') + fp.write(f1) + fp.close() + os.rename(tmpname, name) + +sys.exit(errs) diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/book-shortcuts.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/book-shortcuts.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,3 @@ + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/bookhtml.cfg --- a/fr/bookhtml.cfg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -% -*- latex -*- - -\Preamble{xhtml} - -% Tex4ht's default definition of lists is complete crap. -% Unfortunately, it can't distinguish between "ul" and "dl" lists. - -\ConfigureList{itemize}% - {\EndP\HCode{
    }\let\endItem=\empty} - {\ifvmode \IgnorePar\fi - \EndP\HCode{
}\ShowPar} - {\endItem \def\endItem{\EndP\Tg}\HCode{
  • }} - {\HCode{}} -\def\textbullet{} - -\begin{document} - -\EndPreamble diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/branch.tex --- a/fr/branch.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,392 +0,0 @@ -\chapter{Managing releases and branchy development} -\label{chap:branch} - -Mercurial provides several mechanisms for you to manage a project that -is making progress on multiple fronts at once. To understand these -mechanisms, let's first take a brief look at a fairly normal software -project structure. - -Many software projects issue periodic ``major'' releases that contain -substantial new features. In parallel, they may issue ``minor'' -releases. These are usually identical to the major releases off which -they're based, but with a few bugs fixed. - -In this chapter, we'll start by talking about how to keep records of -project milestones such as releases. We'll then continue on to talk -about the flow of work between different phases of a project, and how -Mercurial can help you to isolate and manage this work. - -\section{Giving a persistent name to a revision} - -Once you decide that you'd like to call a particular revision a -``release'', it's a good idea to record the identity of that revision. -This will let you reproduce that release at a later date, for whatever -purpose you might need at the time (reproducing a bug, porting to a -new platform, etc). -\interaction{tag.init} - -Mercurial lets you give a permanent name to any revision using the -\hgcmd{tag} command. Not surprisingly, these names are called -``tags''. -\interaction{tag.tag} - -A tag is nothing more than a ``symbolic name'' for a revision. Tags -exist purely for your convenience, so that you have a handy permanent -way to refer to a revision; Mercurial doesn't interpret the tag names -you use in any way. Neither does Mercurial place any restrictions on -the name of a tag, beyond a few that are necessary to ensure that a -tag can be parsed unambiguously. A tag name cannot contain any of the -following characters: -\begin{itemize} -\item Colon (ASCII 58, ``\texttt{:}'') -\item Carriage return (ASCII 13, ``\Verb+\r+'') -\item Newline (ASCII 10, ``\Verb+\n+'') -\end{itemize} - -You can use the \hgcmd{tags} command to display the tags present in -your repository. In the output, each tagged revision is identified -first by its name, then by revision number, and finally by the unique -hash of the revision. -\interaction{tag.tags} -Notice that \texttt{tip} is listed in the output of \hgcmd{tags}. The -\texttt{tip} tag is a special ``floating'' tag, which always -identifies the newest revision in the repository. - -In the output of the \hgcmd{tags} command, tags are listed in reverse -order, by revision number. This usually means that recent tags are -listed before older tags. It also means that \texttt{tip} is always -going to be the first tag listed in the output of \hgcmd{tags}. - -When you run \hgcmd{log}, if it displays a revision that has tags -associated with it, it will print those tags. -\interaction{tag.log} - -Any time you need to provide a revision~ID to a Mercurial command, the -command will accept a tag name in its place. Internally, Mercurial -will translate your tag name into the corresponding revision~ID, then -use that. -\interaction{tag.log.v1.0} - -There's no limit on the number of tags you can have in a repository, -or on the number of tags that a single revision can have. As a -practical matter, it's not a great idea to have ``too many'' (a number -which will vary from project to project), simply because tags are -supposed to help you to find revisions. If you have lots of tags, the -ease of using them to identify revisions diminishes rapidly. - -For example, if your project has milestones as frequent as every few -days, it's perfectly reasonable to tag each one of those. But if you -have a continuous build system that makes sure every revision can be -built cleanly, you'd be introducing a lot of noise if you were to tag -every clean build. Instead, you could tag failed builds (on the -assumption that they're rare!), or simply not use tags to track -buildability. - -If you want to remove a tag that you no longer want, use -\hgcmdargs{tag}{--remove}. -\interaction{tag.remove} -You can also modify a tag at any time, so that it identifies a -different revision, by simply issuing a new \hgcmd{tag} command. -You'll have to use the \hgopt{tag}{-f} option to tell Mercurial that -you \emph{really} want to update the tag. -\interaction{tag.replace} -There will still be a permanent record of the previous identity of the -tag, but Mercurial will no longer use it. There's thus no penalty to -tagging the wrong revision; all you have to do is turn around and tag -the correct revision once you discover your error. - -Mercurial stores tags in a normal revision-controlled file in your -repository. If you've created any tags, you'll find them in a file -named \sfilename{.hgtags}. When you run the \hgcmd{tag} command, -Mercurial modifies this file, then automatically commits the change to -it. This means that every time you run \hgcmd{tag}, you'll see a -corresponding changeset in the output of \hgcmd{log}. -\interaction{tag.tip} - -\subsection{Handling tag conflicts during a merge} - -You won't often need to care about the \sfilename{.hgtags} file, but -it sometimes makes its presence known during a merge. The format of -the file is simple: it consists of a series of lines. Each line -starts with a changeset hash, followed by a space, followed by the -name of a tag. - -If you're resolving a conflict in the \sfilename{.hgtags} file during -a merge, there's one twist to modifying the \sfilename{.hgtags} file: -when Mercurial is parsing the tags in a repository, it \emph{never} -reads the working copy of the \sfilename{.hgtags} file. Instead, it -reads the \emph{most recently committed} revision of the file. - -An unfortunate consequence of this design is that you can't actually -verify that your merged \sfilename{.hgtags} file is correct until -\emph{after} you've committed a change. So if you find yourself -resolving a conflict on \sfilename{.hgtags} during a merge, be sure to -run \hgcmd{tags} after you commit. If it finds an error in the -\sfilename{.hgtags} file, it will report the location of the error, -which you can then fix and commit. You should then run \hgcmd{tags} -again, just to be sure that your fix is correct. - -\subsection{Tags and cloning} - -You may have noticed that the \hgcmd{clone} command has a -\hgopt{clone}{-r} option that lets you clone an exact copy of the -repository as of a particular changeset. The new clone will not -contain any project history that comes after the revision you -specified. This has an interaction with tags that can surprise the -unwary. - -Recall that a tag is stored as a revision to the \sfilename{.hgtags} -file, so that when you create a tag, the changeset in which it's -recorded necessarily refers to an older changeset. When you run -\hgcmdargs{clone}{-r foo} to clone a repository as of tag -\texttt{foo}, the new clone \emph{will not contain the history that - created the tag} that you used to clone the repository. The result -is that you'll get exactly the right subset of the project's history -in the new repository, but \emph{not} the tag you might have expected. - -\subsection{When permanent tags are too much} - -Since Mercurial's tags are revision controlled and carried around with -a project's history, everyone you work with will see the tags you -create. But giving names to revisions has uses beyond simply noting -that revision \texttt{4237e45506ee} is really \texttt{v2.0.2}. If -you're trying to track down a subtle bug, you might want a tag to -remind you of something like ``Anne saw the symptoms with this -revision''. - -For cases like this, what you might want to use are \emph{local} tags. -You can create a local tag with the \hgopt{tag}{-l} option to the -\hgcmd{tag} command. This will store the tag in a file called -\sfilename{.hg/localtags}. Unlike \sfilename{.hgtags}, -\sfilename{.hg/localtags} is not revision controlled. Any tags you -create using \hgopt{tag}{-l} remain strictly local to the repository -you're currently working in. - -\section{The flow of changes---big picture vs. little} - -To return to the outline I sketched at the beginning of a chapter, -let's think about a project that has multiple concurrent pieces of -work under development at once. - -There might be a push for a new ``main'' release; a new minor bugfix -release to the last main release; and an unexpected ``hot fix'' to an -old release that is now in maintenance mode. - -The usual way people refer to these different concurrent directions of -development is as ``branches''. However, we've already seen numerous -times that Mercurial treats \emph{all of history} as a series of -branches and merges. Really, what we have here is two ideas that are -peripherally related, but which happen to share a name. -\begin{itemize} -\item ``Big picture'' branches represent the sweep of a project's - evolution; people give them names, and talk about them in - conversation. -\item ``Little picture'' branches are artefacts of the day-to-day - activity of developing and merging changes. They expose the - narrative of how the code was developed. -\end{itemize} - -\section{Managing big-picture branches in repositories} - -The easiest way to isolate a ``big picture'' branch in Mercurial is in -a dedicated repository. If you have an existing shared -repository---let's call it \texttt{myproject}---that reaches a ``1.0'' -milestone, you can start to prepare for future maintenance releases on -top of version~1.0 by tagging the revision from which you prepared -the~1.0 release. -\interaction{branch-repo.tag} -You can then clone a new shared \texttt{myproject-1.0.1} repository as -of that tag. -\interaction{branch-repo.clone} - -Afterwards, if someone needs to work on a bug fix that ought to go -into an upcoming~1.0.1 minor release, they clone the -\texttt{myproject-1.0.1} repository, make their changes, and push them -back. -\interaction{branch-repo.bugfix} -Meanwhile, development for the next major release can continue, -isolated and unabated, in the \texttt{myproject} repository. -\interaction{branch-repo.new} - -\section{Don't repeat yourself: merging across branches} - -In many cases, if you have a bug to fix on a maintenance branch, the -chances are good that the bug exists on your project's main branch -(and possibly other maintenance branches, too). It's a rare developer -who wants to fix the same bug multiple times, so let's look at a few -ways that Mercurial can help you to manage these bugfixes without -duplicating your work. - -In the simplest instance, all you need to do is pull changes from your -maintenance branch into your local clone of the target branch. -\interaction{branch-repo.pull} -You'll then need to merge the heads of the two branches, and push back -to the main branch. -\interaction{branch-repo.merge} - -\section{Naming branches within one repository} - -In most instances, isolating branches in repositories is the right -approach. Its simplicity makes it easy to understand; and so it's -hard to make mistakes. There's a one-to-one relationship between -branches you're working in and directories on your system. This lets -you use normal (non-Mercurial-aware) tools to work on files within a -branch/repository. - -If you're more in the ``power user'' category (\emph{and} your -collaborators are too), there is an alternative way of handling -branches that you can consider. I've already mentioned the -human-level distinction between ``small picture'' and ``big picture'' -branches. While Mercurial works with multiple ``small picture'' -branches in a repository all the time (for example after you pull -changes in, but before you merge them), it can \emph{also} work with -multiple ``big picture'' branches. - -The key to working this way is that Mercurial lets you assign a -persistent \emph{name} to a branch. There always exists a branch -named \texttt{default}. Even before you start naming branches -yourself, you can find traces of the \texttt{default} branch if you -look for them. - -As an example, when you run the \hgcmd{commit} command, and it pops up -your editor so that you can enter a commit message, look for a line -that contains the text ``\texttt{HG: branch default}'' at the bottom. -This is telling you that your commit will occur on the branch named -\texttt{default}. - -To start working with named branches, use the \hgcmd{branches} -command. This command lists the named branches already present in -your repository, telling you which changeset is the tip of each. -\interaction{branch-named.branches} -Since you haven't created any named branches yet, the only one that -exists is \texttt{default}. - -To find out what the ``current'' branch is, run the \hgcmd{branch} -command, giving it no arguments. This tells you what branch the -parent of the current changeset is on. -\interaction{branch-named.branch} - -To create a new branch, run the \hgcmd{branch} command again. This -time, give it one argument: the name of the branch you want to create. -\interaction{branch-named.create} - -After you've created a branch, you might wonder what effect the -\hgcmd{branch} command has had. What do the \hgcmd{status} and -\hgcmd{tip} commands report? -\interaction{branch-named.status} -Nothing has changed in the working directory, and there's been no new -history created. As this suggests, running the \hgcmd{branch} command -has no permanent effect; it only tells Mercurial what branch name to -use the \emph{next} time you commit a changeset. - -When you commit a change, Mercurial records the name of the branch on -which you committed. Once you've switched from the \texttt{default} -branch to another and committed, you'll see the name of the new branch -show up in the output of \hgcmd{log}, \hgcmd{tip}, and other commands -that display the same kind of output. -\interaction{branch-named.commit} -The \hgcmd{log}-like commands will print the branch name of every -changeset that's not on the \texttt{default} branch. As a result, if -you never use named branches, you'll never see this information. - -Once you've named a branch and committed a change with that name, -every subsequent commit that descends from that change will inherit -the same branch name. You can change the name of a branch at any -time, using the \hgcmd{branch} command. -\interaction{branch-named.rebranch} -In practice, this is something you won't do very often, as branch -names tend to have fairly long lifetimes. (This isn't a rule, just an -observation.) - -\section{Dealing with multiple named branches in a repository} - -If you have more than one named branch in a repository, Mercurial will -remember the branch that your working directory on when you start a -command like \hgcmd{update} or \hgcmdargs{pull}{-u}. It will update -the working directory to the tip of this branch, no matter what the -``repo-wide'' tip is. To update to a revision that's on a different -named branch, you may need to use the \hgopt{update}{-C} option to -\hgcmd{update}. - -This behaviour is a little subtle, so let's see it in action. First, -let's remind ourselves what branch we're currently on, and what -branches are in our repository. -\interaction{branch-named.parents} -We're on the \texttt{bar} branch, but there also exists an older -\hgcmd{foo} branch. - -We can \hgcmd{update} back and forth between the tips of the -\texttt{foo} and \texttt{bar} branches without needing to use the -\hgopt{update}{-C} option, because this only involves going backwards -and forwards linearly through our change history. -\interaction{branch-named.update-switchy} - -If we go back to the \texttt{foo} branch and then run \hgcmd{update}, -it will keep us on \texttt{foo}, not move us to the tip of -\texttt{bar}. -\interaction{branch-named.update-nothing} - -Committing a new change on the \texttt{foo} branch introduces a new -head. -\interaction{branch-named.foo-commit} - -\section{Branch names and merging} - -As you've probably noticed, merges in Mercurial are not symmetrical. -Let's say our repository has two heads, 17 and 23. If I -\hgcmd{update} to 17 and then \hgcmd{merge} with 23, Mercurial records -17 as the first parent of the merge, and 23 as the second. Whereas if -I \hgcmd{update} to 23 and then \hgcmd{merge} with 17, it records 23 -as the first parent, and 17 as the second. - -This affects Mercurial's choice of branch name when you merge. After -a merge, Mercurial will retain the branch name of the first parent -when you commit the result of the merge. If your first parent's -branch name is \texttt{foo}, and you merge with \texttt{bar}, the -branch name will still be \texttt{foo} after you merge. - -It's not unusual for a repository to contain multiple heads, each with -the same branch name. Let's say I'm working on the \texttt{foo} -branch, and so are you. We commit different changes; I pull your -changes; I now have two heads, each claiming to be on the \texttt{foo} -branch. The result of a merge will be a single head on the -\texttt{foo} branch, as you might hope. - -But if I'm working on the \texttt{bar} branch, and I merge work from -the \texttt{foo} branch, the result will remain on the \texttt{bar} -branch. -\interaction{branch-named.merge} - -To give a more concrete example, if I'm working on the -\texttt{bleeding-edge} branch, and I want to bring in the latest fixes -from the \texttt{stable} branch, Mercurial will choose the ``right'' -(\texttt{bleeding-edge}) branch name when I pull and merge from -\texttt{stable}. - -\section{Branch naming is generally useful} - -You shouldn't think of named branches as applicable only to situations -where you have multiple long-lived branches cohabiting in a single -repository. They're very useful even in the one-branch-per-repository -case. - -In the simplest case, giving a name to each branch gives you a -permanent record of which branch a changeset originated on. This -gives you more context when you're trying to follow the history of a -long-lived branchy project. - -If you're working with shared repositories, you can set up a -\hook{pretxnchangegroup} hook on each that will block incoming changes -that have the ``wrong'' branch name. This provides a simple, but -effective, defence against people accidentally pushing changes from a -``bleeding edge'' branch to a ``stable'' branch. Such a hook might -look like this inside the shared repo's \hgrc. -\begin{codesample2} - [hooks] - pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch -\end{codesample2} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch00-preface.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch00-preface.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,78 @@ + + +\chapter*{Préface} +\addcontentsline{toc}{chapter}{Préface} +\label{chap:preface} + +La gestion de source distribuée est encore un territoire peu exploré +et qui, par conséquent, a grandi très rapidement grâce à la seule +volonté de ses explorateurs. + +Je rédige un livre sur ce sujet car je crois que c'est un sujet +important qui mérite bien un guide du terrain. J'ai choisi d'écrire +ce livre sur Mercurial car c'est l'outil le plus simple pour découvrir +ce nouveau monde et qu'en outre, il répond très bien au besoin de +réels environnements, là où d'autres outils de gestion de source n'y +parviennent pas. + + +Cet ouvrage est un travail en cours + +Je publie ce livre tout en continuant à l'écrire, dans l'espoir qu'il +vous sera utile. J'espère aussi que les lecteurs pourront ainsi y contribuer +si ils le souhaitent. + + + +À propros des exemples de ce livre + +Ce livre a une approche particulière des exemples d'exécution. Ils sont +toujours dynamiques&emdash;chacun est le résultat d'un script shell qui +exécute les commandes Mercurial que vous voyez. Chaque fois qu'une +image du livre est construite à partir des sources, tous les scripts d'exemple +sont exécutés automatiquement, et les résultats comparés à ceux attendus. + +Cette approche a l'avantage de garantir que les exemples sont toujours +justes ; ils montrent exactement le comportement de la version de +Mercurial spécifiée dans la couverture de ce livre. Si je mets à jour cette +version, et que les commandes changent, la génération du livre échouera. + +Il y a un petit désavantage à cette approche, les dates et les +durées que vous verrez dans ces exemples ont tendances à être +réduits de manière très différente d'une exécution manuelle. Un être humain +ne peut exécuter qu'une commande toutes les secondes, alors que mes scripts +automatisés en exécutent plusieurs en une seule seconde. + +Ainsi, en une seule seconde, plusieurs commits peuvent avoir lieu +au sein d'un exemple. Vous le constatez, entre autres, dans les +exemples sur bisect, dans la section . + +En conséquence, quand vous lisez les exemples, n'accordez pas trop +d'importance aux dates et aux durées d'exécution des commandes. Mais +soyez sûr que le comportement que vous voyez est cohérent et +reproductible. + + + + +Colophon&emdash;Cet ouvrage est libre + +Ce livre est publié sous la licence Open Publication License +\footnote{Pour plus de renseignements : +http://opencontent.org/openpub/{Open Publication License} }, +et est construit uniquement à l'aide de logiciels libres. Il est mis +en forme avec \LaTex{}; et les illustrations sont réalisées avec +Inkscape. + + +L'ensemble des fichiers sources de cet ouvrage est publié dans un +dépot mercurial http://hg.serpentine.com/mercurial/book. + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch01-intro.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch01-intro.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,744 @@ + + + +Introduction +\label{chap:intro} + + +À propos de la gestion source + +La gestion de sources est un processus permettant de gérer différentes +versions de la même information. Dans sa forme la plus simple, c'est +ce que tout le monde fait manuellement : quand vous modifiez +un fichier, vous le sauvegardez sous un nouveau nom contenant un numéro, +à chaque fois plus grand que celui de la version précédente. + +Ce genre de gestion de version manuelle est cependant facilement sujette +à des erreurs, ainsi, depuis longtemps, des logiciels existent pour +résoudre cette problématique. Les premiers outils de gestion de sources +étaient destinés à aider un seul utilisateur, à automatiser la gestion +des versions d'un seul fichier. Dans les dernières décades, cette cible +s'est largement agrandie, ils gèrent désormais de multiples fichiers, et +aident un grand nombre de personnes à travailler ensemble. Les outils les +plus modernes n'ont aucune difficulté à gérer plusieurs milliers de +personnes travaillant ensemble sur des projets regroupant plusieurs +centaines de milliers de fichiers. + + +Pourquoi utiliser un gestionnaire de source ? + +Il y a de nombreuses raisons pour que vous ou votre équipe souhaitiez +utiliser un outil automatisant la gestion de version pour votre projet. + +L'outil se chargera de suivre l'évolution de votre projet, sans +que vous n'ayez à le faire. Pour chaque modification, vous aurez à votre +disposition un journal indiquant qui a fait quoi, pourquoi +ils l'ont fait, quand ils l'ont fait, et ce qu'ils ont +modifiés. + +Quand vous travaillez avec d'autres personnes, les logiciels de +gestion de source facilitent le travail collaboratif. Par exemple, quand +plusieurs personnes font, plus ou moins simultanément, des modifications +incompatibles, le logiciel vous aidera à identifier et à résoudre les conflits. + +L'outil vous aidera à réparer vos erreurs. Si vous effectuez un changement +qui se révèle être une erreur, vous pourrez revenir à une version +antérieure d'un fichier ou même d'un ensemble de fichiers. En fait, un outil de +gestion de source vraiment efficace vous permettra d'identifier à quel +moment le problème est apparu (voir la section pour plus +de détails). + +L'outil vous permettra aussi de travailler sur plusieurs versions différentes +de votre projet et à gérer l'écart entre chacune. + +La plupart de ces raisons ont autant d'importances &emdash;du moins en théorie&emdash; que +vous travailliez sur un projet pour vous, ou avec une centaine d'autres +personnes. + + +Une question fondamentale à propos des outils de gestion de source, qu'il s'agisse +du projet d'une personne ou d'une grande équipe, est quels sont ses +avantages par rapport à ses coûts. Un outil qui est difficile à +utiliser ou à comprendre exigera un lourd effort d'adaptation. + + +Un projet de cinq milles personnes s'effondrera très certainement de lui même +sans aucun processus et outil de gestion de source. Dans ce cas, le coût +d'utilisation d'un logiciel de gestion de source est dérisoire puisque +sans, l'échec est presque garanti. + + +D'un autre coté, un rapide hack d'une personne peut sembler un contexte +bien pauvre pour utiliser un outil de gestion de source, car, bien évidement +le coût d'utilisation dépasse le coût total du projet. N'est ce pas ? + + +Mercurial supporte ces deux échelles de travail. Vous pouvez apprendre +les bases en quelques minutes seulement, et, grâce à sa performance, vous pouvez +l'utiliser avec facilité sur le plus petit des projets. Cette simplicité +signifie que vous n'avez pas de concept obscurs ou de séquence de commandes +défiant l'imagination, sans aucune corrélation avec \emph{ce que vous êtes +vraiment en train de faire}. En même temps, ces mêmes performances et sa +nature peer-to-peer vous permettent d'augmenter, sans difficulté, son +utilisation à de très grands projets. + + +Aucun outil de gestion de source ne peut sauver un projet mal mené, mais un +bon outil peut rendre beaucoup plus fluide votre travail. + + + + +Les multiples noms de la gestion de source + +La gestion de source\footnote{NdT: J'ai utilisé systématiquement le terme +gestion de source à travers tout l'ouvrage. Ce n'est pas forcement la +meilleure traduction, et ceci peut rendre la lecture un peu lourde, mais je +pense que le document y gagne en clarté et en précision.} est un domaine +divers, tellement qu'il n'existe pas une seul nom ou acronyme pour le désigner. +Voilà quelqu'uns des noms ou +acronymes que vous rencontrerez le plus souvent\footnote{NdT: J'ai conservé la +liste des noms en anglais pour des raisons de commodité (ils sont plus +googelable). En outre, j'ai opté pour conserver l'ensemble des opérations de +Mercurial (\textit{commit},\textit{push}, \textit{pull},...) en anglais, là +aussi pour faciliter la lecture d'autres documents en anglais, ainsi que +l'utilisation de Mercurial}. + + +: + + +\textit{Revision control (RCS)} ; + + +Software configuration management (SCM), ou \textit{configuration management} ; + + +\textit{Source code management} ; + + +\textit{Source code control}, ou \textit{source control} ; + + +\textit{Version control (VCS)}. + + + +Certaines personnes prétendent que ces termes ont en fait des sens +différents mais en pratique ils se recouvrent tellement qu'il n'y a pas +réellement de manière pertinente de les distinguer. + + + + + +Une courte histoire de la gestion de source + +Le plus célèbre des anciens outils de gestion de source est \textit{SCCS +(Source Code Control System)}, que Marc Rochkind conçu dans les laboratoires de +recherche de Bell (\textit{Bell Labs}), dans le début des années 70. +\textit{SCCS} ne fonctionnait que sur des fichiers individuels, et obligeait chaque +personne travaillant sur le projet d'avoir un accès à un répertoire de +travail commun, sur le même système. Seulement une seule personne pouvait +modifier un fichier au même moment, ce fonctionnement était assuré par +l'utilisation de verrou (lock). Il était courant que des personnes +verrouillent des fichiers, et plus tard, oublient de le déverrouiller; +empêchant n'importe qui d'autre de travailler sur ces fichiers sans l'aide de +l'administrateur... + + +Walter Tichy a développé une alternative libre à \textit{SCCS} au début des +années 80, qu'il nomma \textit{RSC (Revison Control System)}. Comme +\textit{SCCS}, \textit{RCS} demandait aux développeurs de travailler sur le même +répertoire partagé, et de verrouiller les +fichiers pour se prémunir de tout conflit issu de modifications concurrentes. + + +Un peu plus tard dans les années 1980, Dick Grune utilisa \textit{RCS} comme +une brique de base pour un ensemble de scripts \textit{shell} qu'il intitula +cmt, avant de la renommer en \textit{CVS (Concurrent Versions System)}. La +grande innovation de CVS était que les développeurs pouvaient travailler +simultanément et indépendamment dans leur propre espace de travail. Ces espaces +de travail privés assuraient que les développeurs ne se marchent pas +mutuellement sur les pieds, comme c'était souvent le cas avec RCS et SCCS. +Chaque développeur disposait donc de sa copie de tous les fichiers du projet, +et ils pouvaient donc librement les modifier. Ils devaient néanmoins effectuer +la fusion (\textit{merge}) de leurs fichiers, avant d'effectuer le +commit de leur modifications sur le dépôt central. + + +Brian Berliner reprit les scripts de Grune's et les réécrit en C, qu'il publia +en 1989. Depuis, ce code a été modifié jusqu'à devenir la version moderne de +CVS. CVS a acquis ainsi la capacité de fonctionner en réseau, transformant son +architecture en client/serveur. L'architecture de CVS est centralisée, seul le +serveur a une copie de l'historique du projet. L'espace de travail client ne +contient qu'une copie de la dernière version du projet, et quelques métadonnées +pour indiquer où le serveur se trouve. CVS a été un grand succès, aujourd'hui +il est probablement l'outil de gestion de contrôle le plus utilisé au monde. + + +Au début des années 1990, Sun Microsystmes développa un premier outil de +gestion de source distribué, nommé TeamWare. Un espace de travail TeamWare +contient une copie complète de l'historique du projet. TeamWare n'a pas de +notion de dépôt central. (CVS utilisait RCS pour le stockage de l'historique, +TeamWare utilisait SCCS). + + +Alors que les années 1990 avançaient, les utilisateurs ont pris conscience d'un +certain nombre de problèmes avec CVS. Il enregistrait simultanément des +modifications sur différents fichiers individuellement, au lieu de les +regrouper dans une seule opération cohérente et atomique. Il ne gère pas bien +sa hiérarchie de fichier, il est donc assez aisé de créer le chaos en renommant +les fichiers et les répertoires. Pire encore, son code source est difficile à +lire et à maintenir, ce qui agrandit largement le niveau de souffrance +associé à la réparation de ces problèmes d'architecture de manière prohibitive. + + +En 2001, Jim Blandy et Karl Fogel, deux développeurs qui avaient travaillé sur +CVS, initièrent un projet pour le remplacer par un outil qui aurait une +meilleure architecture et un code plus propre. Le résultat, Subversion, ne +quitte pas le modèle centralisé et client/server de CVS, mais ajoute les +opérations de commit atomique sur de multiples fichiers, une meilleure +gestion des espaces de noms, et d'autres fonctionnalités qui en font un +meilleur outil que CVS. Depuis sa première publication, il est rapidement +devenu très populaire. + + +Plus ou moins simultanément, Graydon Hoare a commencé sur l'ambitieux +système de gestion distribué Monotone. Bien que Monotone corrige plusieurs +défauts de CVS's tout en offrant une architecture peer-to-peer, il va aussi +plus loin que la plupart des outils de révision de manière assez innovante. Il +utilise des hash cryptographiques comme identifiants, et il a une notion +complète de confiance du code issu des différentes sources. + + +Mercurial est né en 2005. Bien que très influencé par Monotone, Mercurial se +concentre sur la facilité d'utilisation, les performances et la capacité à +monter en charge pour de très gros projets. + + + + +Tendances de la gestion de source + +Il y a eu une tendance évidente dans le développement et l'utilisation d'outils +de gestion de source depuis les quatre dernières décades, au fur et à mesure +que les utilisateurs se sont habitués à leur outils et se sont sentis contraints +par leurs limitations. + + +La première génération commença simplement par gérer un fichier unique sur un +ordinateur individuel. Cependant, même si ces outils présentaient une grande +avancée par rapport à la gestion manuelle des versions, leur modèle de +verrouillage et leur utilisation limitée à un seul ordinateur rendaient leur +utilisation possible uniquement dans une très petite équipe. + + +La seconde génération a assoupli ces contraintes en adoptant une architecture +réseau et centralisée, permettant de gérer plusieurs projets entiers en même +temps. Alors que les projets grandirent en taille, ils rencontrèrent de nouveaux +problèmes. Avec les clients discutant régulièrement avec le serveurs, la montée +en charge devint un réel problème sur les gros projets. Une connexion réseau +peu fiable pouvait complètement empêcher les utilisateurs distants de dialoguer +avec le serveur. Alors que les projets \textit{Open Source} commencèrent à +mettre en place des accès en lecture seule disponible anonymement, les +utilisateurs sans les privilèges de commit réalisèrent qu'ils ne pouvaient +pas utiliser les outils pour collaborer naturellement avec le projet, comme ils +ne pouvaient pas non plus enregistrer leurs modifications. + + +La génération actuelle des outils de gestion de source est peer-to-peer par +nature. Tout ces systèmes ont abandonné la dépendance à un serveur central, et +ont permis à leur utilisateur de distribuer les données de leur gestion de +source à qui en a besoin. La collaboration à travers Internet a transformé la +contrainte technologique en une simple question de choix et de consencus. Les +outils modernes peuvent maintenant fonctionner en mode déconnecté sans limite et +de manière autonome, la connexion au réseau n'étant nécessaire que pour +synchroniser les modifications avec les autres dépôts. + + + + +Quelques avantages des gestionnaires de source distribués + +Même si les gestionnaire de source distribués sont depuis plusieurs années +assez robustes et aussi utilisables que leurs prédécesseurs, les utilisateurs +d'autres outils n'y ont pas encore été sensibilisés. Les gestionnaires +de source distribués se distinguent particulièrement de leurs équivalents +centralisés de nombreuses manières. + + +Pour un développeur individuel, ils restent beaucoup plus rapides que les +outils centralisés. Cela pour une raison simple : un outil centralisé doit +toujours dialoguer à travers le réseau pour la plupart des opérations, car +presque toutes les métadonnées sont stockées sur la seule copie du serveur +central. Un outil distribué stocke toute ses métadonnées localement. À tâche +égale, effectuer un échange avec le réseau ajoute un délai aux outils +centralisés. Ne sous-estimez pas la valeur d'un outil rapide : vous allez +passer beaucoup de temps à interagir avec un logiciel de gestion de source. + + +Les outils distribués sont complètement indépendants des aléas de votre serveur, +d'autant plus qu'ils répliquent les métadonnées à beaucoup d'endroits. Si +votre serveur central prend feu, vous avez intérêt à ce que les médias de +sauvegardes soient fiables, et que votre dernier backup soit récent et +fonctionne sans problème. Avec un outil distribué, vous avez autant de +backup que de contributeurs. + + +En outre, la fiabilité de votre réseau affectera beaucoup moins les +outils distribués. Vous ne pouvez même pas utiliser un outil centralisé +sans connexion réseau, à l'exception de quelques commandes, très limitées. +Avec un outil distribué, si votre connexion réseau tombe pendant que vous +travaillez, vous pouvez ne même pas vous en rendre compte. La seule chose +que vous ne serez pas capable de faire sera de communiquer avec des dépôts +distants, opération somme toute assez rare en comparaison aux opérations +locales. Si vous avez une équipe de collaborateurs très dispersée ceci peut +être significatif. + + + +Avantages pour les projets \textit{Open Source} + +Si vous prenez goût à un projet \textit{Open Source} et que vous +décidez de commencer à toucher à son code, et que le projet utilise +un gestionnaire de source distribué, vous êtes immédiatement un "pair" +avec les personnes formant le cœur du projet. Si ils publient +leurs dépôts, vous pouvez immédiatement copier leurs historiques de +projet, faire des modifications, enregistrer votre travail en utilisant +les même outils qu'eux. Par comparaison, avec un outil centralisé, vous +devez utiliser un logiciel en mode lecture seule à moins que +quelqu'un ne vous donne les privilèges de commit sur le serveur +central. Avant ça, vous ne serez pas capable d'enregistrer vos +modifications, et vos propres modifications risqueront de se +corrompre chaque fois que vous essayerez de mettre à jour à votre +espace de travail avec le serveur central. + + + +Le non-problème du \textit{fork} + +Il a été souvent suggéré que les gestionnaires de source distribués +posent un risque pour les projets \textit{Open Source} car ils +facilitent grandement la création de fork\footnote{NdT:Création +d'une +version alternative du logiciel{http://fr.wikipedia.org/wiki/Fork#Embranchement_d.27un_projet_informatique}.} +Un fork apparait quand il y des divergences d'opinion ou d'attitude +au sein d'un groupe de développeurs qui aboutissent à la décision de ne +plus travailler ensemble. Chaque parti s'empare d'une copie plus ou moins +complète du code source du projet et continue dans sa propre direction. + + +Parfois ces différents partis décident de se réconcilier. Avec un +serveur central, l'aspect technique de cette réconciliation +est un processus douloureux, et essentiellement manuel. Vous devez +décider quelle modification est la gagnante, et replacer, par un +moyen ou un autre, les modifications de l'autre équipe dans l'arborescence +du projet. Ceci implique généralement la perte d'une partie de l'historique +d'un des partis, ou même des deux. + + +Ce que les outils distribués permettent à ce sujet est probablement +la meilleure façon de développer un projet. Chaque modification +que vous effectuez est potentiellement un fork. La grande force de +cette approche est que les gestionnaires de source distribués doivent être +vraiment très efficaces pour fusionner\footnote{NdT:j'ai choisi de +traduire ici \textit{merging} par fusionner pour des raisons de clarté} +des forks, car les forks, dans ce contexte, arrivent tout le +temps. + + +Si chaque altération que n'importe qui effectue, à tout moment, est vue +comme un fork à fusionner, alors ce que le monde de l'\textit{Open +Source} voit comme un fork devient uniquement une problématique +sociale. En fait, les outils de gestions de source distribués réduisent +les chances de fork: + + +Ils éliminent la distinction sociale qu'imposent les outils centralisés + entre les membres du projets (ceux qui ont accès au commit) et ceux de + l'extérieur (ce qui ne l'ont pas). \item Ils rendent plus facile la + réconciliation après un fork social, car + tout ce qu'elle implique est une simple fusion. + + + +Certaines personnes font de la résistance envers les gestionnaires de source +distribués parce qu'ils veulent garder un contrôle ferme sur leur projet, et +ils pensent que les outils centralisés leur fournissent ce contrôle. Néanmoins, +si c'est votre cas, sachez que si vous publiez votre dépôt CVS ou Subversion +de manière publique, il existe une quantité d'outils disponibles pour récupérer +entièrement votre projet et son historique (quoique lentement) et le récréer +ailleurs, sans votre contrôle. En fait, votre contrôle sur votre projet est +illusoire, vous ne faites qu'interdire à vos collaborateurs de travailler +de manière fluide, en disposant d'un miroir ou d'un fork de votre +historique. +%%%TODO: Fussy, those last sentences are not really well translated: +%%%no problem for me (wilk) +%However, if you're of this belief, and you publish your CVS or Subversion +%repositories publically, there are plenty of tools available that can pull +%out your entire project's history (albeit slowly) and recreate it somewhere +%that you don't control. So while your control in this case is illusory, you are +%forgoing the ability to fluidly collaborate with whatever people feel +%compelled to mirror and fork your history. + + + + + +Avantages pour les projets commerciaux + +Beaucoup de projets commerciaux sont réalisés par des équipes éparpillées +à travers le globe. Les contributeurs qui sont loin du serveur central +devront subir des commandes lentes et même parfois peu fiables. Les +solutions propriétaires de gestion de source tentent de palier ce problème +avec des réplications de sites distants qui sont à la fois coûteuses à mettre +en place et lourdes à administrer. Un système distribué ne souffre pas +de ce genre de problèmes. En outre, il est très aisé de mettre en place +plusieurs serveurs de références, disons un par site, de manière à ce qu'il +n'y ait pas de communication redondante entre les dépôts, sur une connexion +longue distance souvent onéreuse. + + +Les systèmes de gestion de source supportent généralement assez mal la +montée en charge. Ce n'est pas rare pour un gestionnaire de source centralisé +pourtant onéreux de s'effondrer sous la charge combinée d'une douzaine +d'utilisateurs concurrents seulement. Une fois encore, la réponse à cette problématique +est généralement encore la mise en place d'un ensemble complexe de serveurs +synchronisés par un mécanisme de réplication. Dans le cas d'un gestionnaire +de source distribué, la charge du serveur central &emdash; si vous avez un&emdash; est +plusieurs fois inférieure (car toutes les données sont déjà répliquées ailleurs), +un simple serveur, pas très cher, peut gérer les besoins d'une plus grande +équipe, et la réplication pour balancer la charge devient le +travail d'un simple script. + + +Si vous avez des employés sur le terrain, en train de chercher à résoudre un souci sur +le site d'un client, ils bénéficieront aussi d'un gestionnaire de source +distribué. Cet outil leur permettra de générer des versions personnalisées, +d'essayer différentes solutions, en les isolant aisément les unes des autres, +et de rechercher efficacement à travers l'historique des sources, la cause +des bugs ou des régressions, tout ceci sans avoir besoin de la moindre +connexion au réseau de votre compagnie. + + + + + +Pourquoi choisir Mercurial? + +Mercurial a plusieurs caractéristiques qui en font un choix particulièrement +pertinent pour la gestion de source: + + + \item Il est facile à apprendre et à utiliser ; + \item Il est léger et performant ; + \item Il monte facilement en charge ; + \item Il est facile à personnaliser ; + + + +Si vous êtes déjà familier d'un outil de gestion de source, vous serez +capable de l'utiliser en moins de 5 minutes. Sinon, ça ne sera pas beaucoup +plus long\footnote{NdT: Pour appuyer le propos de l'auteur, je signale que +j'utilise Mercurial comme outil d'initiation à la gestion de contrôle dans +des travaux pratiques à l'ESME Sudria (http://www.esme.fr) et que les +élèves le prennent en main sans difficulté majeure malgré l'approche distribuée.}. +Les commandes utilisées par Mercurial, comme ses fonctionnalités, sont +généralement uniformes et cohérentes, et vous pouvez donc ainsi garder en tête +simplement quelques règles générales, plutôt qu'un lot complexe d'exceptions. + + +Sur un petit projet, vous pouvez commencer à travailler avec Mercurial en +quelques instants. Ajouter des modifications ou des branches, transférer +ces modifications (localement ou via le réseau), et les opérations +d'historique ou de statut sont aussi très rapides. Mercurial reste hors de +votre chemin grâce à sa simplicité d'utilisation et sa rapidité d'exécution. + + +L'utilité de Mercurial ne se limite pas à de petits projets: il est +aussi utilisé par des projets ayant des centaines ou même des milliers +de contributeurs, avec plusieurs dizaines de milliers de fichiers, et des +centaines de méga de code source. + + +Voici une liste non exhaustive des projets complexes ou critiques utilisant +Mercurial : +%TODO +% For both spanish and english version, add the following examples: + + + \item Firefox{https://developer.mozilla.org/en/Mozilla_Source_Code_(Mercurial)} ; + \item OpenSolaris{http://opensolaris.org/os/community/tools/scm/hg_help/} ; + \item OpenJDK{http://hg.openjdk.java.net/} (utilisant en outre l'extension + forest pour gérer ses sous modules); + + + +Si les fonctionnalités cœur de Mercurial ne sont pas suffisantes pour vous, +il est très aisé d'en construire d'autres. Mercurial est adapté à l'utilisation +de scripts, et son implémentation interne en Python, propre et claire, +rend encore plus facile l'ajout de fonctionnalités sous forme d'extensions. Il +en existe déjà un certain nombre de très populaires et très utiles, +dont le périmètre va de la recherche de bugs à l'amélioration des performances. + + + + +Mercurial comparé aux autres outils + +Avant que vous n'alliez plus loin, comprenez bien que cette section +reflète mes propres expériences, et elle est donc (j'ose le dire) +peu objective. Néanmoins, j'ai utilisé les outils de gestion de source +listés ci dessous, dans la plupart des cas, pendant plusieurs années. +%% TODO: Fussy translation. + + + +Subversion + +Subversion est un des outils de gestion de source les plus populaire, il fût +développé pour remplacer CVS. Il a une architecture client/server centralisée. + + +Subversion et Mercurial ont des noms de commandes très similaires pour +les mêmes opérations, ainsi si vous êtes familier avec l'un, c'est facile +d'apprendre l'autre. Ces deux outils sont portables sur les systèmes +d'exploitation les plus populaires\footnote{NdT:Mercurial fonctionne sans problème +sur OpenVMS à l'ESME Sudria http://www.esme.fr, compte tenu que Subversion a été +développé en C, je ne suis pas sûr que son portage aurait été aussi aisé.}. +%TODO: Backport this statement in english and spanish + + +Avant la version 1.5, Subversion n'offrait aucune forme de support pour les fusions. Lors +de l'écriture de ce livre, ses capacités de fusion étaient nouvelles, et réputées pour être +\href{http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword}{complexes +et bugguées}. + + +Mercurial dispose d'un avantage substantiel en terme de performance par rapport à +Subversion sur la plupart des opérations que j'ai pu tester. J'ai mesuré +une différence de performance allant de deux à six fois plus rapide avec +le système de stockage de fichier local de Subversion 1.4.3 +(ra_local), qui est la méthode d'accès la plus rapide disponible. Dans +un déploiement plus réaliste, impliquant un stockage réseau, Subversion +serait encore plus désavantagé. Parce que la plupart des commandes Subversion +doivent communiquer avec le serveur et que Subversion n'a pas de mécanisme +de réplication, la capacité du serveur et la bande passante sont devenues des +goulots d'étranglement pour les projets de taille moyenne ou grande. + + +En outre, Subversion implique une surcharge substantielle dans le stockage local +de certaines données, pour éviter des transactions avec le serveur, pour +certaines opérations communes, telles que la recherche des fichiers modifiés +(status) et l'affichage des modifications par rapport à la révision +courante (diff). En conséquence, un répertoire de travail Subversion +a souvent la même taille, ou est plus grand, qu'un dépôt Mercurial et son +espace de travail, et ceci bien que le dépôt Mercurial contienne l'intégralité +de l'historique. + + +Subversion est largement supporté par les outils tierces. Mercurial est +actuellement encore en retrait de ce point de vue. L'écart se réduit, néanmoins, +et en effet certains des outils graphiques sont maintenant supérieurs à leurs +équivalents Subversion. Comme Mercurial, Subversion dispose d'un excellent +manuel utilisateur. + + +Parce que Subversion ne stocke pas l'historique chez ses clients, il est +parfaitement adapté à la gestion de projets qui doivent suivre un ensemble +de larges fichiers binaires et opaques. Si vous suivez une cinquantaine de +versions d'un fichier incompressible de 10MB, l'occupation disque coté client +d'un projet sous Subversion restera à peu près constante. A l'inverse, +l'occupation disque du même projet sous n'importe lequel des gestionnaires +de source distribués grandira rapidement, proportionnellement aux nombres +de versions, car les différences entre chaque révisions seront très grandes. + + +En outre, c'est souvent difficile ou, généralement, impossible de fusionner +des différences dans un fichier binaire. La capacité de Subversion de +verrouiller des fichiers, pour permettre à l'utilisateur d'être le seul +à le mettre à jour (commit) temporairement, est un avantage significatif +dans un projet doté de beaucoup de fichiers binaires. + + +Mercurial peut importer l'historique depuis un dépôt Subversion. Il peut +aussi exporter l'ensemble des révisions d'un projet vers un dépôt Subversion. +Ceci rend très facile de prendre la température et d'utiliser Mercurial et Subversion +en parallèle, avant de décider de migrer vers Mercurial. La conversion de +l'historique est incrémentale, donc vous pouvez effectuer une conversion +initiale, puis de petites additions par la suite pour ajouter les nouvelles +modifications. + + + + +Git + +Git est un outil de gestion de source distribué qui fût développé pour gérer +le code source de noyau de Linux. Comme Mercurial, sa conception initiale a +été inspirée par Monotone. + + +Git dispose d'un ensemble conséquent de commandes, avec plus de 139 commandes +individuelles pour la version 1.5.0. Il a aussi la réputation d'être difficile +à apprendre. Comparé à Git, le point fort de Mercurial est clairement sa +simplicité. + + +En terme de performance, Git est extrêmement rapide. Dans la plupart des +cas, il est plus rapide que Mercurial, tout du moins sur Linux, alors que +Mercurial peut être plus performant sur d'autres opérations. Néanmoins, sur +Windows, les performances et le niveau de support général fourni par Git, +au moment de l'écriture de cet ouvrage, est bien derrière celui de Mercurial. + + +Alors que le dépôt Mercurial ne demande aucune maintenance, un dépôt Git +exige d'exécuter manuellement et régulièrement la commande repacks sur +ces métadonnées. Sans ceci, les performances de git se dégradent et la +consommation de l'espace disque augmente rapidement. Un serveur qui contient +plusieurs dépôts Git qui ne sont pas régulièrement et fréquemment repacked +deviendra un vrai problème lors des backups du disque, et il y eu des +cas, où un backup journalier pouvait durer plus de 24 heures. Un dépôt +fraichement repacked sera légèrement plus petit qu'un dépôt Mercurial, +mais un dépôt non repacked est beaucoup plus grand. + + +Le cœur de Git est écrit en C. La plupart des commandes Git sont implémentées +sous forme de scripts Shell ou Perl, et la qualité de ces scripts varie +grandement. J'ai plusieurs fois constaté que certains de ces scripts étaient +chargés en mémoire aveuglément et que la présence d'erreurs pouvait s'avérer +fatal. + + +Mercurial peut importer l'historique d'un dépôt Git. + + + + +CVS + +CVS est probablement l'outil de gestion de source le plus utilisé aujourd'hui +dans le monde. À cause de son manque de clarté interne, il n'est plus +maintenu depuis plusieurs années. + + +Il a une architecture client/serveur centralisée. Il ne regroupe pas les +modifications de fichiers dans une opération de commit atomique, ce +qui permet à ses utilisateurs de casser le \textit{build} assez +facilement : une personne peut effectuer une opération de commit +sans problème puis être bloquée par besoin de fusion, avec comme conséquence +néfaste, que les autres utilisateurs ne récupèreront qu'une partie de ses +modifications. Ce problème affecte aussi la manière de travailler avec +l'historique du projet. Si vous voulez voir toutes les modifications d'une +personne du projet, vous devrez injecter manuellement les descriptions et les +\textit{timestamps} des modifications de chacun des fichiers impliqués (si +vous savez au moins quels sont ces fichiers). + + +CVS a une notion étrange des \textit{tags} et des branches que je n'essayerai +même pas de décrire ici. Il ne supporte pas bien les opérations de renommage d'un +fichier ou d'un répertoire, ce qui facilite la corruption de son dépôt. Il n'a +presque pas pour ainsi dire de contrôle de cohérence interne, il est donc +pratiquement impossible de dire si un dépôt est corrompu ni à quel point. Je +ne recommanderai pas CVS pour un projet existant ou nouveau. + + +Mercurial peut importer l'historique d'un projet CVS. Néanmoins, il y a +quelques principes à respecter; ce qui est vrai aussi pour les autres +outils d'import de projet CVS. À cause de l'absence de commit atomique +et gestion de version de l'arborescence, il n'est pas possible de reconstruire +de manière précise l'ensemble de l'historique. Un travail de devinette +est donc nécessaire, et les fichiers renommés ne sont pas détectés. Parce +qu'une bonne part de l'administration d'un dépôt CVS est effectuée manuellement, +et est donc, sujette à erreur, il est courant que les imports CVS rencontrent +de nombreux problèmes avec les dépôt corrompus (des \textit{timestamps} +de révision complètement buggés et des fichiers verrouillés depuis des années +sont deux des problèmes les moins intéressants dont je me souvienne). + + +Mercurial peut importer l'historique depuis un dépôt CVS. + + + + +Outils propriétaires + +Perforce a une architecture client/serveur centralisée, sans aucun +mécanisme de mise en cache de données coté client. Contrairement à la plupart +des outils modernes de gestion de source, Perforce exige de ses +utilisateurs d'exécuter une commande pour informer le serveur +central de tout fichier qu'ils souhaitent modifier. + + +Les performances de Perforce sont plutôt bonnes pour des petites +équipes, mais elles s'effondrent rapidement lorsque le nombre +d'utilisateurs augmente au delà de la douzaine. Des installations +de Perforce assez larges nécessitent le déploiement de proxies pour +supporter la montée en charge associée. + + + + +Choisir un outil de gestion de source + +A l'exception de CVS, tous les outils listés ci-dessus ont des +forces qui leur sont propres et qui correspondent à certaines +formes de projet. Il n'y a pas un seul meilleur outil de gestion +de source qui correspondrait le mieux à toutes les situations. + + +Par exemple, Subversion est un très bon choix lorsqu'on travaille +avec beaucoup de fichiers binaires, qui évoluent régulièrement, grâce +à sa nature centralisée et sa capacité à verrouiller des fichiers. + + +Personnellement, je préfère Mercurial pour sa simplicité, ses +performances et sa bonne capacité de fusion, et il m'a très bien rendu service +de plusieurs années maintenant. + + + + + +Migrer depuis un outil à Mercurial + +Mercurial est livré avec une extension nommée convert, qui +peut de manière incrémentale importer des révisions depuis différents +autres outils de gestion de source. Par incrémental, j'entends que +vous pouvez convertir l'historique entier du projet en une seule fois, +puis relancer l'outil d'import plus tard pour obtenir les modifications +effectuées depuis votre import initial. + + +Les outils de gestion de source supportés par convert sont : + + + \item Subversion + \item CVS + \item Git + \item Darcs + + + +En outre, convert peut exporter les modifications depuis Mercurial +vers Subversion. Ceci rend possible d'essayer Subversion en parallèle +avant de choisir une solution définitive, sans aucun risque de perte de +données. + + +La commande convert est très simple à utiliser. Simplement, +indiquez le chemin ou l'URL du dépôt de source, en lui indiquant éventuellement +le nom du chemin de destination, et la conversion se met en route. Après cet +import initial, il suffit de relancer la commande encore une fois pour +importer les modifications effectuées depuis. + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch02-tour-basic.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch02-tour-basic.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,889 @@ + + + +Un rapide tour de Mercurial +\label{chap:tour-basic} + + +Installer Mercurial sur votre système +\label{sec:tour:install} + +Des paquetages binaires de Mercurial sont disponibles pour la plupart +des systèmes d'exploitation, ce qui rend facile l'utilisation immédiate +de Mercurial sur votre ordinateur. + + +Linux + +Parce que chaque distribution de Linux a ses propres outils de gestion +de paquets, politiques et rythmes de développements, il est difficile de +donner un ensemble d'instructions uniques pour installer les binaires de +Mercurial. La version de Mercurial avec laquelle vous vous retrouverez +dépendra grandement de l'activité de la personne en charge du paquetage +pour la distribution. + +Pour rester simple, je me concentrerai sur l'installation de Mercurial +en ligne de commande, sous les distributions les plus courantes. La +plupart des distributions fournissent des gestionnaires graphiques de +paquetage qui vous permettront d'installer Mercurial en quelques clicks. +Le paquetage devrait se nommer \textit{mercurial}. + + +Debian: + + apt-get install mercurial + + +Fedora Core: + + + yum install mercurial + + + + + +Gentoo: + + + emerge mercurial + + + + + +OpenSUSE: + + + yum install mercurial + + + + + +Ubuntu: Le paquetage de Mercurial d'Ubuntu est construit sur celui de Debian. + Pour l'installer, exécutez simplement les commandes suivantes: + + + apt-get install mercurial + + + Les paquetages Ubuntu pour Mercurial ont tendance à être un peu en retard + par rapport au paquetage Debian (au moment de l'écriture de ce livre, il + faut compter à peu près un retard de 7 mois), ce qui signifie que parfois + sur Ubuntu, vous risquez de rencontrer des problèmes qui ont été corrigés + depuis longtemps dans les paquetages Debian. + + + + + +Solaris + +SunFreeWare, à http://www.saufreeware.com, est une bonne source +pour trouver un vaste nombre de paquets précompilés pour 32 ou 64 bits +Intel et les architecture Sparc, dont les versions courantes de Mercurial. + + + + +Mac OS X + +Lee Cantey publie un installateur de Mercurial pour Mac OS X sur le site +http://mercurial.berkwood.com. Ce paquetage fonctionne sur les +architectures Intel- et PowerPC. Avant de vous en servir, vous devez +installer une version Universelle MacPython web:macpython. C'est +assez facile à faire : suivez simplement les instructions sur le site +de Lee. + + +Il est aussi possible d'installer Mercurial en utilisant Fink ou MacPorts, +deux outils de gestion de paquetage libres pour Mac OS X. Si vous avez +Fink, utilisez sudo fink install mercurial-py25. Si vous avez +MacPorts, sudo port install mercurial. + + + + +Windows + +Lee Cantey publie aussi un installateur de Mercurial pour Windows sur le site +http://mercurial.berkwood.com. Ce paquetage n'a aucune dépendance +externe, il fonctionne tout court. + + + + La version de Windows de Mercurial ne convertie pas automatiquement + les retours chariot Windows et Unix. Si vous désirez partager votre + travail avec des utilisateurs Unix, vous devez faire un peu de configuration + supplémentaire. XXX En dire plus. + + + + + + +Commencer à utiliser Mercurial + +Pour commencer, nous utiliserons la commande hg version pour vérifier +si Mercurial est installé proprement. Les informations affichées sur la +version ne sont pas réellement importantes en soit, c'est surtout de savoir +si elles s'affichent qui nous intéresse. + + + + +L'aide intégrée + +Mercurial fournit un système d'aide intégré, ce qui est inestimable quand +vous vous retrouvez coincé à essayer de vous rappeler comment lancer telle +ou telle commande. +Si c'est le cas, exécutez simplement hg help; il vous aidera à imprimer +une brève liste de commandes, avec une description de ce qu'elles font. Si vous +demandez de l'aide sur une commande spécifique (voir ci-dessous), il affichera +des informations plus détaillées. + +Pour un niveau d'informations encore plus détaillées (ce dont vous aurez rarement +besoin), exécuter hg help . L'option est +l'abréviation de , et indique à Mercurial d'afficher plus +d'informations que d'habitude. + + + + + +Travailler avec un dépôt + +Avec Mercurial, tout se déroule au sein du dépôt\footnote{NdT: Dépôt est +la traduction que j'ai retenue pour tout l'ouvrage du terme anglais \textit{repository}}. +Le dépôt d'un projet contient tous les fichiers qui appartiennent au projet. + + +Il n'y a rien de particulièrement magique au sujet de ce dépôt, c'est +simplement une arborescence sur votre système de fichiers que Mercurial +traite de manière spéciale. Vous pouvez renommer ou effacer ce répertoire +à n'importe quel moment, en utilisant la ligne de commande ou votre +explorateur de fichiers. + + + +Faire une copie locale de votre dépôt + +Copier un dépôt est juste un peu spécial. Bien que vous +puissiez utiliser une commande habituelle de copie pour copier +votre dépôt, il vaut mieux utiliser une commande fournie par +Mercurial. Cette commande est appelée hg clone, car elle +crée une copie identique à un dépôt existant. + +Si votre opération de clonage réussit, vous devriez maintenant +avoir un répertoire local appelé hello. Ce répertoire +contiendra quelques fichiers. + +Ces fichiers ont le même contenu et historique dans votre dépôt +qu'ils ont dans le dépôt que vous avez cloné. + + +Chaque dépôt Mercurial est complet, autonome et indépendant. Il +contient sa propre copie privée des fichiers du projet et de leur +historique. Le clone d'un dépôt se souvient de la localisation du +dépôt à partir duquel il a été clôné, mais il ne communique pas avec +ce dernier, ou un autre, à moins que vous ne lui demandiez. + + +Ce que tout ceci signifie pour le moment est que nous sommes libres +d'expérimenter avec ce dépôt, confiants dans le fait qu'il s'agit d'un +bac à sable qui n'affectera personne d'autre. + + + + +Quel est le contenu d'un dépôt ? + +Prêtons plus attention un instant au contenu d'un dépôt. Nous voyons +qu'il contient un répertoire nommé .hg. C'est ici que Mercurial +conserve toutes ses métadonnées. + + + +Le contenu du répertoire .hg et ses sous répertoires sont les +seuls propres à Mercurial. Tous les autres fichiers et répertoires dans +le dépôt sont à vous, et vous pouvez en faire ce que vous voulez. + + +Pour introduire un peu de terminologie, le répertoire .hg est +un vrai dépôt, et tous les fichiers et les répertoires qui coexistent +avec lui, sont désignés sous le nom espace de travail\footnote{NdT: +\textit{working directory}}. Une manière facile de se rappeler cette +distinction est de retenir que le dépôt contient l'historique +de votre projet, alors que l'espace de travail contient une \emph{copie +ponctuelle}\footnote{NdT: Ce terme est une traduction du terme anglais +\textit{snapshot}. Il est traduit ici pour faciliter la lecture, mais ne sera +plus traduit par la suite.} de votre projet à un certain point de son +historique. + + + + + +Une ballade dans l'historique + +Une des premières choses que vous aurez envie de faire avec un nouveau +dépôt, sera de comprendre son historique. La commande hg log vous +donne une vue de l'historique. + +Par défaut, cette commande affiche à l'écran un bref paragraphe pour chaque +révision enregistrée pour ce projet. Dans la terminologie de Mercurial, nous +appelons chacun de ces évènements enregistrés un changeset, parce +qu'il contient un ensemble de modifications sur plusieurs fichiers. + + +La commande hg log affiche ainsi ces informations: + + +changeset: Ce champ contient un nombre, séparé par deux points + (:), d'une chaine hexadécimale. Il s'agit en fait d'identifiants + d'un \textit{changeset}. Il y a deux identifiants car le numéro de + la révision est plus court et plus à facile à saisir qu'une séquence + hexadécimale. + + +user: L'identité de la personne qui a créée ce %%% laisser le terme anglais car il sera affiché + \textit{changeset}. C'est un champ libre de forme, mais la plupart du + temps il contient le nom et l'email de la personne. + + +date: La date et l'heure à laquelle le \textit{changeset} + a été créé, ainsi que le \textit{fuseau horaire} dans laquelle il a été créé. %%%TODO: Translate 'timezone' properly : FUSEAU + (La date et l'heure sont locales à ce \textit{fuseau}, elles indiquent + donc quelle date et heure il était pour la personne qui a créé ce %%%TODO: je suppose (quelle "heure") OUI + \textit{changeset}.) + + +résumé: La première du message que le créateur a associé à + son \textit{changeset} pour le décrire. + + + +Par défaut, la commande hg log n'affiche qu'un résumé, il manque +beaucoup de détails. + + +La figure fournit une représentation graphique +de l'historique du dépôt hello, pour rendre plus facile de voir +dans quelle direction l'historique se déroule\footnote{NdT: \textit{flowing in}.}. +Nous reviendrons régulièrement sur cette représentation dans ce chapitre et +ceux qui suivent. + + + + + XXX add text + Représentation graphique du dépôt hello + \label{fig:tour-basic:history} + + + + +Changesets, révisions, et discuter avec les autres +%%% je propose "colaboration" + + +Comme l'anglais est réputé pour être un langage maladroit, et que l'informatique +est la source de bien des erreurs de terminologies (pourquoi utiliser un +seul terme quand quatre feront l'affaire ?), la gestion de version a une +variété de mots et de phrases qui veulent dire la même chose. Si vous +discutez d'historique de Mercurial avec d'autres personnes, +%%%TODO: ça ne veut rien dire: il faut supprimer une des personnes : soit "quelqu'un", +% soit "à d'autres personnes" +vous constaterez que souvent le mot \textit{changeset} est contracté simplement +en change ou (à l'écrit) cset, et même parfois un +\textit{changeset} simplement révision, abrégé en rev. + + +Bien que le mot que vous utilisez pour désigner le concept de +\textit{changeset} importe peu, l'identifiant que vous utilisez +pour désigner un changeset \textit{spécifique} a une grande +importance. Rappelez vous que le champ \textit{changeset} affiché par la +commande hg log identifie un \textit{changeset} à la fois avec +un numéro de révision et une séquence hexadécimale. + + + +Le numéro de révision est seulement valable dans ce dépôt, + + +alors que la séquence hexadécimale est un \emph{identifiant + permanent, et invariant } qui pourra toujours être associé au + \textit{changeset} exact de chaque copie de votre dépôt. + + + +La distinction est importante. Si vous envoyez un email à quelqu'un en +parlant de la révision 33, il est très probable que sa révision 33 +ne sera pas la même que la votre. La raison de ceci est que le +numéro de révision dépend de l'ordre dans lequel les modifications sont +arrivées dans le dépôt, et il n'y a aucune garantie que les mêmes changements +soient arrivés dans le même ordre dans différents dépôts. Trois modifications +$a,b,c$ peuvent aisément apparaitre dans un dépôt comme $0,1,2$, et dans +un autre comme $1,0,2$. + + +Mercurial utilise les numéros de révision uniquement comme des raccourcis +pratiques. Si vous devez discuter d'un \textit{changeset} avec quelqu'un, +ou identifer un \textit{changeset} pour une quelquonque %%%TODO: our : "pour" ou "ou" +raison (par exemple, un rapport de \textit{bug}), utilisez la séquence +hexadécimale. + + + + +Afficher une révision spécifique + +Pour réduire la sortie de hg log à une seule révision, utilisez +l'option (ou ). Vous pouvez utiliser +le numéro de révision ou la séquence hexadécimale comme identifiant, et +demander autant de révisions que vous le souhaitez. + + + +Si vous voulez voir l'historique de plusieurs révisions sans avoir à +les énumérer, vous pouvez utiliser la \textit{range notation} +\footnote{NdT: Il n'est pas aisé de traduire ce terme, donc je le %%%TODO : intervalle de numérotation ? +laisse en anglais} qui vous permet d'exprimer l'idée je veux toutes +les révisions entre $a$ et $b$, inclus. + +Mercurial respecte aussi l'ordre dans lequel vous spécifiez les +révisions, ainsi hg log -r 2:4 affichera $2,3,4$ alors que +hg log -r 4:2 affichera $4,3,2$. + + + + +Informations détaillées + + +Le résumé affiché par hg log est suffisant si vous savez déjà ce %%%TODO: je pense que le premier "si" est de trop : exact +que vous cherchez. En revanche, vous aurez probablement besoin de voir une description +complète du changement, ou une liste des fichiers modifiés si vous +cherchez à déterminer qu'un \textit{changeset} est bien celui que vous%%%TODO: les propositions sont mal construites : après un "si...." il faut une proposition sans "si... donc ici : "si ... recherchez", ben quoi ? +recherchez. L'option \hgopt{-v} de la commande hg log (ou +\hgopt{--verbose}) vous donne ces informations supplémentaires. + + + +Si vous voulez voir à la fois la description et le contenu d'une +modification, ajouter l'option (ou ). +Ceci affiche le contenu d'une modification comme un diff unifié +\footnote{NdT: \textit{unified diff}} (si vous n'avez jamais vu de diff +unifié avant, consultez la section pour un rapide +survol). + + + + + + + + +Tout sur les options de commandes + + +Avant d'aller plus loin sur le fonctionnement des commandes de Mercurial, +étudions un moment comment elles fonctionnent de manière générale. Vous +trouverez ça probablement utile pour la suite de notre parcours. + + +Mercurial utilise une approche directe et cohérente pour interpréter %%%TODO: une manière d'approche ? +les options que vous passez aux commandes. Il suit une convention commune +à la plupart des systèmes Unix et Linux modernes. + + + +Chaque option a un nom complet. Par exemple, comme nous l'avons déjà + vu, la commande hg log accepte l'option .%%%TODO: commande ou command e\hgcmd...? + + +La plupart des options disposent de noms abrégés. Aussi, au lieu d'utiliser + , vous pouvez utiliser . (Les options qui + n'ont pas de noms abrégés sont généralement rarement utilisées, pour cette raison). + + +Les noms complets commencent par deux tirets (i.e. ), + alors que les options courtes commencent avec un seul (i.e. ). + + +Les noms des options sont cohérents entre les commandes. Par exemple, + chaque commande qui accepte un \textit{changeset ID} ou un numéro de révision + accepte aussi et comme arguments. + %TODO: Small mistake here, shouldn't have log here... shouldn't it ? + + + +Dans les exemples de ce livre, j'utilise les noms abrégés plutôt que les noms +complets. Ceci est une préférence personnelle, pas une recommandation. + + +La plupart des commandes qui affichent une quelconque sortie à l'écran, +afficheront davantage avec l'option (ou ), et +moins avec l'option (ou ). + + + + +Faire et vérifier des modifications + +Maintenant que nous avons une bonne idée des commandes pour consulter +l'historique de Mercurial, regardons comment faire des modifications et +les examiner. + + + +La première chose que nous allons faire c'est isoler notre expérience dans +un dépôt à part. Nous allons utiliser la commande hg clone, mais nous +n'avons pas besoin de faire une copie de dépôt distant. Comme nous avons +déjà une copie locale, nous pouvons juste faire un clone de celle-ci à la +place. C'est beaucoup plus rapide que de faire une copie à travers le +réseau, et un dépôt cloné localement prend également moins d'espace disque. + + + + + +On notera au passage qu'il est souvent considéré comme une bonne pratique +de conserver une copie immaculée du dépôt distant, à partir de laquelle +vous pourrez faire des copies locales temporaires pour créer des bacs à +sable pour chaque tâche sur laquelle vous souhaitez travailler. Ceci vous +permet de travailler sur plusieurs choses en parallèle, chacune isolée les +unes des autres en attendant que ces tâches soient finies et que vous soyez +prêt à les réintégrer. Parce que les copies locales sont peu coûteuses, il +est très rapide de créer ou détruire des dépôts dès que vous en avez besoin. + + +%% Note: la dernière phrase n'est pas une traduction littérale, mais je +%% pense qu'elle exprime plus clairement en français ce que veut dire son +%% équivalent anglais. : OUI + + +Dans notre dépôt my-hello, nous avons un fichier hello.c +qui contient le classique programme hello, world. Nous allons utiliser +l'ancienne et vénérable commande sed pour l'éditer afin qu'il +affiche une seconde ligne à l'écran. (J'utilise sed seulement parce +qu'il est ainsi facile d'écrire des exemples sous forme de script. Comme +vous n'avez pas ces contraintes, vous n'utiliserez probablement pas sed +mais plutôt votre éditeur de texte favori). + + + + + +La commande hg status de Mercurial nous dira de quels fichiers Mercurial +s'occupe au sein de ce dépôt. + +La commande hg status n'affiche rien sur la sortie pour quelques fichiers +mais une ligne commence par M for hello.c. À moins que +vous ne lui indiquiez de le faire, hg status n'affichera aucune sortie +pour les fichiers qui n'ont pas été modifiés. + + +Le caractère M indique que Mercurial a remarqué que nous avions +modifié le fichier hello.c. Nous n'avons pas besoin d'informer +Mercurial que nous allons modifier un fichier avant de le faire, ou que nous +venons de le modifier, il est capable de s'en rendre compte tout seul. + + +C'est pratique de savoir que nous avons modifié hello.c, mais il +serait encore plus pratique de savoir ce que nous avons modifié exactement. Pour +cela, nous avons la commande hg diff. + + + + + + + +Enregister les modifications dans un nouveau \textit{changeset} + +Nous pouvons modifier des fichiers, compiler et tester nos modifications, +et utiliser les commandes hg status et hg diff pour voir les +modifications effectuées, jusqu'au moment où nous serons assez satisfaits +pour décider d'enregistrer notre travail dans un \textit{changeset}. + + +La commande hg commit vous laisse créer un nouveau \textit{changeset}, +nous désignerons généralement cette opération par faire un commit ou +commiter\footnote{NdT: De mon expérience, la plupart des francophones +utilisent régulièrement, à l'oral, cette expression, mais bien évidement +il ne s'agit pas d'un terme de terminologie correcte, ni même français.} + + + +Définir le nom d'utilisateur + +Quand vous exécutez la commande hg commit pour la première fois, elle +n'est pas garantie de réussir. Mercurial enregistre votre nom et votre +adresse avec chaque modification que vous effectuez, de manière à ce que +vous soyez capable (ou d'autres le soient) de savoir qui a fait quelle +modification. Mercurial essaye automatiquement de découvrir un nom +d'utilisateur qui ait un minimum de sens pour effectuer l'opération +de \textit{commit} avec. Il va essayer chacune des méthodes suivantes, +dans l'ordre: + + +Si vous spécifiez l'option avec la commande + hg commit, suivi d'un nom d'utilisateur, ceci aura toujours la + priorité sur les autres méthodes ci dessous. + + +Si vous avez défini une variable d'environnement HGUSER, + c'est cette valeur qui est alors utilisée. + + +Si vous créez un fichier nommé .hgrc dans votre + répertoire \textit{home}, avec une entrée username, + c'est la valeur associée qui sera utilisée. Pour voir à quoi + ressemble le contenu de ce fichier regardez la + section ci-dessous. + + +Si vous avez défini une variable d'environnement EMAIL + celle ci sera utilisée ensuite. + + +Enfin, Mercurial interrogera votre système pour trouver votre + nom d'utilisateur local ainsi que le nom de la machine hôte, et il + fabriquera un nom d'utilisateur à partir de ces données. Comme il arrive + souvent que ce genre de noms soit totalement inutile, il vous + préviendra en affichant un message d'avertissement. + + + +Si tous ces mécanismes échouent, Mercurial n'exécutera pas la commande, +affichant un message d'erreur. Dans ce cas, il ne vous laissera pas +effectuer de \textit{commit} tant que vous n'aurez pas défini un nom +d'utilisateur. + + +Vous devriez penser à utiliser la variable d'environement HGUSER +et l'option comme moyen pour \emph{changer le nom +d'utilisateur} par défaut. Pour une utilisation normale, la manière la plus +simple et robuste d'opérer est de créer un fichier .hgrc, +voir ci-dessous pour les détails à ce sujet. + + + +Créer un fichier de configuration pour Mercurial +\label{sec:tour-basic:username} + + +Pour définir un nom d'utilisateur, utilisez votre éditeur de texte favori +pour créer un fichier .hgrc dans votre répertoire \textit{home}. +Mercurial va utiliser ce fichier pour retrouver votre configuration personnelle. +Le contenu initial devrait ressembler à ceci: + + + # This is a Mercurial configuration file. + [ui] + username = Firstname Lastname <email.address@domain.net> + + +La ligne avec [ui] commence une section du fichier de +configuration, ainsi la ligne username = ... signifie +définir la valeur de l'élément username dans la section +ui. Une section continue jusqu'à ce qu'une nouvelle +commence, ou que la fin du fichier soit atteinte. Mercurial ignore +les lignes vides et traite tout texte situé à la suite d'un +# jusqu'à la fin de la ligne comme un commentaire. + + + + +Choisir un nom d'utilisateur + +Vous pouvez utiliser n'importe quelle valeur pour votre username, +car cette information est destinée à d'autres personnes et non à être +interprétée par Mercurial. La convention que la plupart des personnes +<<<<<<< local +suivent est d'utiliser leurs noms suivies de leurs adresses emails, +comme montrée ci-dessus: + + + + Le mécanisme interne du serveur \textit{web} intégré à Mercurial, + masque les adresses emails, pour rendre plus difficile leurs + récupérations par les outils utilisés par les \textit{spammmers}. + Ceci réduit la probabilité que de recevoir encore plus de + \textit{spam} si vous vous publiez un dépôt sur internet. + + + + + + +Rédiger un message de \textit{commit} + +Lorsqu'on effectue une opération de \textit{commit}, Mercurial +lance automatiquement un éditeur de texte pour permettre de saisir +un message qui décrira les modifications effectuées dans ce +\textit{changeset}. Ce message est nommé le \emph{message de +\textit{commit}}. Ce sera un enregistrement pour tout lecteur +expliquant le pourquoi et le comment de vos modifications, et il sera +affiché par la commande hg log. + + + +L'éditeur que la commande hg commit déclenche ne contiendra +qu'une ligne vide suivi d'un certain nombre de lignes commençant +par HG:. + + + empty line + HG: changed hello.c + + +Mercurial ignore les lignes qui commencent avec HG:, il +ne les utilise que pour nous indiquer quels fichiers modifiés il se +prépare à \textit{commiter}. Modifier ou effacer ces lignes n'a +aucune conséquence sur l'opération de \textit{commit}. + + + + +Rédiger un message \textit{approprié} + +Comme hg log n'affiche que la première ligne du message de +\textit{commit} par défaut, il est souvent considéré comme une bonne +pratique de rédiger des messages de \textit{commit} qui tiennent +sur une seule ligne. Voilà un exemple concret de message de +\textit{commit} qui ne suit pas cette directive, et qui a donc +un résumé peu lisible. + + + + changeset: 73:584af0e231be + user: Censored Person <censored.person@example.org> + date: Tue Sep 26 21:37:07 2006 -0700 + summary: include buildmeister/commondefs. Add an exports and install + + + +A ce sujet, il faut noter qu'il n'existe pas de règle absolue dans ce +domaine. Mercurial lui-même n'interprète pas les contenus des messages +de \textit{commit}, ainsi votre projet est libre de concevoir différentes +politiques de mise en page des messages. + + +Ma préférence personnelle va au message court, mais informatif, qui offre +des précisions supplémentaires par rapport à ce que pourrait m'apprendre une commande +hg log --patch. + + + + +Annuler un \textit{commit} + +Si, en rédigeant le message, vous décidez que finalement vous ne +voulez pas effectuer ce \textit{commit}, il suffit de quitter simplement +l'éditeur sans sauver. Ceci n'aura aucune conséquence sur le dépôt ou +les fichiers de l'espace de travail. + + +Si vous exécuter la commande hg commit sans aucun argument, elle +enregistre toutes les modifications que vous avez faites, comme le montre +les commandes hg status et hg diff. + + + + +Admirer votre travail + +Une fois que votre \textit{commit} est terminé, vous pouvez utiliser +la commande hg tip pour afficher le \textit{changeset} que nous +venons de créer. Cette commande produit une sortie à l'écran qui est +identique à celle du hg log, mais qui n'affiche que la dernière +révision du dépôt. + +On fait couramment référence à la dernière révision du dépôt comme +étant la révision \textit{tip}, ou plus simplement le \textit{tip}. + + + + + +Partager ses modifications + +Nous avons mentionné plus haut que les dépôts de Mercurial +sont autosuffisants. Ce qui signifie que le \textit{changeset} +que vous venez de créer existe seulement dans votre répertoire +my-hello. Étudions comment propager cette modification +dans d'autres dépôts. + + + +Récupérer les modifications d'autres dépôts +\label{sec:tour:pull} + + +Pour commencer, construisons un clone de notre dépôt hello +qui ne contiendra pas le changement que nous venons d'effectuer. Nous +l'appellerons notre dépôt temporaire hello-pull. + + + + + +Nous allons utiliser la commande hg pull pour apporter les +modifications depuis my-hello dans hello-pull. +Néanmoins, récupérer aveuglement des modifications depuis un dépôt +a quelque chose d'un peu effrayant. Mercurial propose donc une +commande hg incoming qui permet de savoir quelles modifications +la commande hg pull pourrait entraîner dans notre dépôt, +et ceci sans effectuer réellement de modification dessus. + +(Bien évidement, quelqu'un pourrait ajouter des modifications +supplémentaires sur le dépôt que nous étudions avec hg incoming, +avant que nous ayons effectué notre hg pull, avec comme +triste conséquence que nous aurons récupéré des modifications que +nous n'attendions pas.) + + +Apporter les modifications rapatriées dans un dépôt se résume donc +à exécuter la commande hg pull, et préciser depuis quel dépôt +effectuer le hg pull. + + + +Comme vous le voyez avec une sortie avant et après de la commande +hg tip, nous avons réussi à récupérer aisément les modifications +dans notre dépôt. Il reste néanmoins quelque chose à faire avant de +placer ces modifications dans l'espace de travail. + + + + +Mise à jour de l'espace de travail + +Nous avons jusqu'à maintenant grossièrement définie la relation +entre un dépôt et un espace de travail. La commande hg pull que +nous avons exécutée dans la section a apporté +des modifications, que nous avons vérifiées, dans notre dépôt, mais +il n'y a aucune trace de ces modifications dans notre espace de travail. +En effet, hg pull ne touche pas (par défaut) à l'espace de +travail. C'est la commande hg update qui s'en charge. + + + +Il peut sembler un peu étrange que la commande hg pull ne mette +pas à jour l'espace de travail automatiquement. Il y a en fait une +très bonne raison à cela : vous pouvez utilisez la commande + + +hg update pour mettre à jour votre espace de travail à l'état +dans lequel il était à n'importe quelle révision de l'historique +du dépôt. Si vous aviez un espace de travail contenant une ancienne +révision&emdash;pour chercher l'origine d'un \textit{bug}, par exemple&emdash;et +que vous effectuiez un hg pull qui mettrait à jour automatiquement +votre espace de travail, vous ne seriez probablement pas très satisfait. + + +Néanmoins, comme les opérations de \textit{pull} sont très souvent +suivies d'un \textit{update}, Mercurial vous permet de combiner les +deux aisément en passant l'option à la commande +hg pull + + + hg pull -u + + + +Si vous étudiez de nouveau la sortie de la commande hg pull dans +la section quand nous l'avons exécutée sans l'option +, vous pouvez constater qu'elle a affiché un rappel assez +utile : vous devez encore effectuer une opération pour mettre à jour +votre espace de travail: + + + + (run 'hg update' to get a working copy) + + + +Pour découvrir sur quelle révision de l'espace de travail on est, utilisez +la commande hg parents. + +Si vous regardez de nouveau le dessin , vous +<<<<<<< local +verrez les flèches reliant entre eux les \textit{changeset}. Le nœud +d'où la flèche part est dans chaque cas un parent, +et le nœud où la flèche arrive est un enfant. + + +L'espace de travail a un parent de la même manière, c'est ce \textit{changeset} +que l'espace de travail contient à ce moment. +%%%TODO : difficile à comprendre : l'espace de travail a un parent, de la même manière, c'est ce changeset que l'espace... + + +Pour mettre à jour l'espace de travail d'une révision particulière, +indiquez un numéro de révision ou un \textit{changeset ID} à la commande +hg update. + +Si vous ne précisez pas de manière explicite de numéro de révision +la commande hg update mettra à jour votre espace de travail avec +le contenu de la révsion \textit{tip}, comme montré dans l'exemple +ci dessus lors du second appel à hg update. + + + + +Transférer les modifications à un autre dépôt + +Mercurial vous laisse transférer les modifications à un autre +dépôt, depuis votre dépôt actuel. Comme dans l'exemple du +hg pull ci-dessus, nous allons créer un dépôt temporaire +vers lequel transférer\footnote{NdT: Les francophones disent souvent +pousser tout simplement} nos modifications. + +La commande hg outgoing nous indique quels changements nous +allons transférer vers l'autre serveur ? + +Et la commande hg push effectue réellement le transfert. + +Comme avec hg pull, la commande hg push ne met pas à jour +le répertoire de travail du dépôt dans lequel il transfère les +modifications. (À l'inverse de hg pull, hg push ne fournit +pas d'option -u pour forcer la mise à jour de l'espace +de travail cible). + + +Qu'est ce qui se passe lorsque vous essayez de récupérer ou de transférer +vos modifications et que le dépôt cible a déjà reçu ces modifications ? +Rien de bien excitant. + + + + + +Partager ses modifications à travers le réseau + +Les commandes que nous avons étudiées dans les sections précédentes +ne sont pas limitées aux dépôt locaux. Chacune fonctionne de la même +manière à travers une connexion réseau, il suffit de lui passer une +URL à la place d'un chemin de fichier local. + + + +Dans cet exemple, nous allons voir quels changements nous pourrions +transférer vers le dépôt distant, mais le dépôt est, de manière tout +à fait compréhensible, pas configuré pour accepter des modifications +d'utilisateurs anonymes. + + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch03-tour-merge.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch03-tour-merge.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,384 @@ + + + +Un rapide tour de Mercurial: fusionner les travaux +\label{chap:tour-merge} + +Nous avons maintenant étudié comment cloner un dépôt, effectuer +des changements dedans, et récupérer ou transférer depuis un +autre dépôt. La prochaine étape est donc de fusionner les +modifications de différents dépôts. + + +Fusionner différents travaux + %%% for 'Merging streams of work' ? +La fusion\footnote{NdT: Je garde fusion mais le jargon professionnel +emploiera généralement le terme \textit{merge}.} est un aspect +fondamental lorsqu'on travaille avec un gestionnaire de source +distribué. + +Alice et Bob ont chacun une copie personnelle du dépôt d'un + projet sur lequel ils collaborent. Alice corrige un \textit{bug} + dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le + sien. Ils veulent un dépôt partagé avec à la fois le correctif du + \textit{bug} et la nouvelle fonctionnalité. + +Je travaille régulièrement sur plusieurs tâches différentes sur + un seul projet en même temps, chacun isolé dans son propre dépôt. + Travailler ainsi signifie que je dois régulièrement fusionner une + partie de mon code avec celui des autres. + + +Parce que la fusion est une opération si commune à réaliser, +Mercurial la rend facile. Étudions ensemble le déroulement des opérations. +Nous commencerons encore par faire un clone d'un autre dépôt (vous voyez +que l'on fait ça tout le temps ?) puis nous ferons quelques modifications +dessus. + +Nous devrions avoir maintenant deux copies de hello.c avec +des contenus différents. Les historiques de ces deux dépôts ont aussi +divergés, comme illustré dans la figure . + + + + + + XXX add text + Historiques récent divergents des dépôts \dirname{my-hello + et my-new-hello} + \label{fig:tour-merge:sep-repos} + + +Nous savons déjà que récupérer les modifications depuis notre dépôt +my-hello n'aura aucun effet sur l'espace de travail. + + + + + +Néanmoins, la commande hg pull nous indique quelque chose au +sujet des heads. + + + +\textit{Head changesets} + +Une \textit{head}\footnote{NdT: Je garde \textit{head} que j'accorde +au féminin comme la coutume orale l'a imposé.} est un \textit{changeset} +sans descendants, ou enfants, comme on les désigne parfois. La révision +\textit{tip} est une \textit{head}, car la dernière révision dans un dépôt +n'a aucun enfant, mais il est important de noter qu'un dépôt peut contenir +plus d'une \textit{head}. + + + + + XXX add text + \caption{Contenu d'un dépôt après avoir transféré le contenu du dépôt + my-hello dans le dépôt my-new-hello} + \label{fig:tour-merge:pull} + + + +Dans la figure , vous pouvez constater l'effet +d'un \textit{pull} depuis le dépôt my-hello dans le dépôt +my-new-hello. L'historique qui était déjà présent dans le dépôt +my-new-hello reste intact, mais une nouvelle révision a été +ajoutée. En vous reportant à la figure , +vous pouvez voir que le \textit{changeset ID} reste le même dans +le nouveau dépôt, mais que le numéro de révision reste le même. +(Ceci est un parfait exemple de pourquoi il n'est fiable d'utiliser les +numéros de révision lorsque l'on discute d'un \textit{changeset}.) Vous +pouvez voir les \texit{heads} présentes dans le dépôt en utilisant la +commande hg heads. + + + + + +Effectuer la fusion + +Que se passe-t-il quand vous essayez d'utiliser la commande hg update +pour mettre à jour votre espace de travail au nouveau \textit{tip}. + +Mercurial nous prévient que la commande hg update n'effectuera pas +la fusion, il ne veut pas mettre à jour l'espace de travail quand il +estime que nous pourrions avoir besoin d'une fusion, à moins de lui +forcer la main. À la place, il faut utiliser la commande hg merge +pour fusionner les deux \textit{heads}. + + + + + + XXX add text + \caption{Espace de travail et dépôt lors d'une fusion, et dans le + \textit{commit} qui suit.} + \label{fig:tour-merge:merge} + + + +Ceci met à jour l'espace de travail de manière à ce qu'il contienne +les modifications des deux \textit{heads}, ce qui apparaît dans +les sorties de la commande hg parents et le contenu de +hello.c. + + + + + +Effectuer le \textit{commit} du résultat de la fusion + +Dès l'instant où vous avez effectué une fusion, hg parents vous +affichera deux parents, avant que vous n'exécutiez la commande +hg commit sur le résultat de la fusion. + +Nous avons maintenant un nouveau \textit{tip}, remarquer qu'il contient +à la fois nos anciennes \textit{heads} et leurs parents. Ce sont +les mêmes révisions que nous avions affichées avec la commande +hg parents. + + + +Dans la figure , vous pouvez voir une représentation +de ce qui se passe dans l'espace de travail pendant la fusion, et comment ceci +affecte le dépôt lors du \textit{commit}. Pendant la fusion, l'espace de travail, +qui a deux \texit{changesets} comme parents, voit ces derniers devenir le parent +%%% TODO: le parent ou "les parents" : plus logique mais si il reste seulement +%%% un changeset, alors c'est effectivement un parent (le changeset est hermaphrodite) +d'un nouveau \textit{changeset}. + + + + + +Fusionner les modifications en conflit + +La plupart des fusions sont assez simple à réaliser, mais parfois +vous vous retrouverez à fusionner des fichiers où la modification touche +la même portion de code, au sein d'un même fichier. À moins que ces +modification ne soient identiques, ceci aboutira à un conflit, +et vous devrez décider comment réconcilier les différentes modifications +dans un tout cohérent. + + + + + XXX add text + Modifications conflictuelles dans un document + \label{fig:tour-merge:conflict} + + + +La figure illustre un cas de modifications +conflictuelles dans un document. Nous avons commencé avec une version simple +de ce fichier, puis nous avons ajouté des modifications, pendant que +quelqu'un d'autre modifiait le même texte. Notre tâche dans la résolution +du conflit est de décider à quoi le fichier devrait ressembler. + + +Mercurial n'a pas de mécanisme interne pour gérer les conflits. +À la place, il exécute un programme externe appelé hgmerge. +Il s'agit d'un script shell qui est embarqué par Mercurial, vous +pouvez le modifier si vous le voulez. Ce qu'il fait par défaut est +d'essayer de trouver un des différents outils de fusion qui seront +probablement installés sur le système. Il commence par les outils +totalement automatiques, et si ils échouent (parce que la résolution +du conflit nécessite une intervention humaine) ou si ils sont absents, +le script tente d'exécuter certains outils graphiques de fusion. + + +Il est aussi possible de demander à Mercurial d'exécuter un autre +programme ou un autre script au lieu de la commande hgmerge, +en définissant la variable d'environnement HGMERGE avec le nom +du programme de votre choix. + + + +Utiliser un outil graphique de fusion + +Mon outil de fusion préféré est kdiff3, que j'utilise ici +pour illustrer les fonctionnalités classiques des outils graphiques +de fusion. Vous pouvez voir une capture d'écran de l'utilisation de +kdiff3 dans la figure . Cet outil +effectue une fusion \textit{three-way}, car il y a trois différentes +versions du fichier qui nous intéresse. Le fichier découpe la partie +supérieure de la fenêtre en trois panneaux: + + + +A gauche on la version de base du fichier, soit la plus + récente version des deux versions qu'on souhaite fusionner. + + +Au centre, il y a notre version du fichier, avec le contenu + que nous avons modifié. + + +Sur la droite, on trouve leur version du fichier, celui qui + contient le \textit{changeset} que nous souhaitons intégré. + + + +Dans le panneau en dessous, on trouve le résultat actuel de notre +fusion. Notre tâche consiste donc à remplacement tous les textes en rouges, +qui indiquent des conflits non résolus, avec une fusion manuelle et pertinente +de notre version et de la leur. + + +Tous les quatre panneaux sont accrochés ensemble, si nous déroulons +les ascenseurs verticalement ou horizontalement dans chacun d'entre eux, les +autres sont mis à jour avec la section correspondante dans leurs fichiers +respectifs. + + + + + XXX add text + Utilisation de \command{kdiff3 pour fusionner différentes versions + d'un fichier.} + \label{fig:tour-merge:kdiff3} + + + +Pour chaque portion de fichier posant problème, nous pouvons choisir +de résoudre le conflit en utilisant une combinaison +de texte depuis la version de base, la notre, ou la leur. Nous pouvons +aussi éditer manuellement les fichiers à tout moment, si c'est +nécessaire. + + +Il y a beaucoup d'outils de fusion disponibles, bien trop pour +en parler de tous ici. Leurs disponibilités varient selon les plate formes +ainsi que leurs avantages et inconvénients. La plupart sont optimisé pour +la fusion de fichier contenant un texte plat, certains sont spécialisé +dans un format de fichier précis (généralement XML). + + + + +Un exemple concret + +Dans cet exemple, nous allons reproduire la modification de l'historique +du fichier de la figure ci dessus. Commençons +par créer un dépôt avec une version de base de notre document. + + + +Créons un clone de ce dépôt et faisons une modification dans le fichier. + +Et un autre clone, pour simuler que quelqu'un d'autre effectue une +modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare +de devoir effectuer des \textit{merge} avec vos propres travaux quand +vous isolez les tâches dans des dépôts distincts. En effet, vous +aurez alors à trouver et résoudre certains conflits). + +Maintenant que ces deux versions différentes du même fichier sont +créées, nous allons configurer l'environnement de manière appropriée pour +exécuter notre \textit{merge}. + + + +Dans cette exemple, je n'utiliserais pas la commande Mercurial +habituelle hgmerge pour effectuer le \textit{merge}, +car il me faudrait abandonner ce joli petit exemple automatisé +pour utiliser un outil graphique. À la place, je vais définir +la variable d'environnement HGMERGE pour indiquer à +Mercurial d'utiliser la commande non-interactive merge. +Cette dernière est embarquée par de nombreux systèmes à la Unix. +Si vous exécutez cet exemple depuis votre ordinateur, ne vous +occupez pas de définir HGMERGE. + +Parce que merge ne peut pas résoudre les modifications +conflictuelles, il laisse des marqueurs de différences +\footnote{NdT: Oui, je traduis \textit{merge markers} par un sens +inverse en Français, mais je pense vraiment que c'est plus clair +comme ça...} à l'intérieur du fichier qui a des conflits, indiquant +clairement quelles lignes sont en conflits, et si elles viennent de +notre fichier ou du fichier externe. + + +Mercurial peut distinguer, à la manière dont la commande merge +se termine, qu'elle n'a pas été capable d'effectuer le \textit{merge}, +alors il nous indique que nous devons effectuer de nouveau cette +opération. Ceci peut être très utile si, par exemple, nous exécutons un +outil graphique de fusion et que nous le quittons sans nous rendre compte +qu'il reste des conflits ou simplement par erreur. + + +Si le \textit{merge} automatique ou manuel échoue, il n'y a rien pour +nous empêcher de corriger le tir en modifiant nous même les fichiers, +et enfin effectuer le \textit{commit} du fichier: + + + + + + +Simplification de la séquence pull-merge-commit +\label{sec:tour-merge:fetch} + + +La procédure pour effectuer la fusion indiquée ci-dessus est simple, +mais requiert le lancement de trois commandes à la suite. + + + hg pull + hg merge + hg commit -m 'Merged remote changes' + + + +Lors du \textit{commit} final, vous devez également saisir un message, +qui aura vraisemblablement assez peu d'intérêt. + + +Il serait assez sympathique de pouvoir réduire le nombre d'opérations +nécessaire, si possible. De fait Mercurial est fourni avec une +extension appelé fetch qui fait justement cela. + + +Mercurial fourni un mécanisme d'extension flexible qui permet à chacun +d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial +léger et facile à utiliser. Certains extensions ajoutent de nouvelles +commandes que vous pouvez utiliser en ligne de commande, alors que +d'autres travaillent en coulisse, par exemple en ajoutant des +possibilités au serveur. + + +L'extension fetch ajoute une nouvelle commande nommée, sans +surprise, hg fetch. Cette extension résulte en une combinaison +de hg pull, hg update and hg merge. Elle commence par +récupérer les modifications d'un autre dépôt dans le dépôt courant. +Si elle trouve que les modifications ajoutent une nouvelle \textit{head}, +elle effectue un \textit{merge}, et ensuite \texit{commit} le résultat +du \textit{merge} avec un message généré automatiquement. Si aucune +\textit{head} n'ont été ajouté, elle met à jour le répertoire de travail +au niveau du nouveau \textit{changeset} \textit{tip}. + + + +Activer l'extension fetch est facile. Modifiez votre .hgrc, +et soit allez à la section extensions soit créer une +section extensions. Ensuite ajoutez une ligne qui consiste +simplement en \Verb+fetch =. + + + + [extensions] + fetch = + + +(Normalement, sur la partie droite de = devrait apparaître +le chemin de l'extension, mais étant donné que l'extension fetch +fait partie de la distribution standard, Mercurial sait où la trouver.) + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch04-concepts.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch04-concepts.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,710 @@ + + + +Behind the scenes +\label{chap:concepts} + +Unlike many revision control systems, the concepts upon which +Mercurial is built are simple enough that it's easy to understand how +the software really works. Knowing this certainly isn't necessary, +but I find it useful to have a mental model of what's going on. + +This understanding gives me confidence that Mercurial has been +carefully designed to be both safe and efficient. And +just as importantly, if it's easy for me to retain a good idea of what +the software is doing when I perform a revision control task, I'm less +likely to be surprised by its behaviour. + +In this chapter, we'll initially cover the core concepts behind +Mercurial's design, then continue to discuss some of the interesting +details of its implementation. + + +Mercurial's historical record + + +Tracking the history of a single file + +When Mercurial tracks modifications to a file, it stores the history +of that file in a metadata object called a filelog. Each entry +in the filelog contains enough information to reconstruct one revision +of the file that is being tracked. Filelogs are stored as files in +the .hg/store/data directory. A filelog contains two kinds +of information: revision data, and an index to help Mercurial to find +a revision efficiently. + +A file that is large, or has a lot of history, has its filelog stored +in separate data (.d suffix) and index (.i +suffix) files. For small files without much history, the revision +data and index are combined in a single .i file. The +correspondence between a file in the working directory and the filelog +that tracks its history in the repository is illustrated in +figure . + + + + XXX add text + \caption{Relationships between files in working directory and + filelogs in repository} + \label{fig:concepts:filelog} + + + + +Managing tracked files + +Mercurial uses a structure called a manifest to collect +together information about the files that it tracks. Each entry in +the manifest contains information about the files present in a single +changeset. An entry records which files are present in the changeset, +the revision of each file, and a few other pieces of file metadata. + + + +Recording changeset information + +The changelog contains information about each changeset. Each +revision records who committed a change, the changeset comment, other +pieces of changeset-related information, and the revision of the +manifest to use. + + + + +Relationships between revisions + +Within a changelog, a manifest, or a filelog, each revision stores a +pointer to its immediate parent (or to its two parents, if it's a +merge revision). As I mentioned above, there are also relationships +between revisions across these structures, and they are +hierarchical in nature. + + +For every changeset in a repository, there is exactly one revision +stored in the changelog. Each revision of the changelog contains a +pointer to a single revision of the manifest. A revision of the +manifest stores a pointer to a single revision of each filelog tracked +when that changeset was created. These relationships are illustrated +in figure . + + + + + XXX add text + Metadata relationships + \label{fig:concepts:metadata} + + + +As the illustration shows, there is not a one to one +relationship between revisions in the changelog, manifest, or filelog. +If the manifest hasn't changed between two changesets, the changelog +entries for those changesets will point to the same revision of the +manifest. If a file that Mercurial tracks hasn't changed between two +changesets, the entry for that file in the two revisions of the +manifest will point to the same revision of its filelog. + + + + + +Safe, efficient storage + +The underpinnings of changelogs, manifests, and filelogs are provided +by a single structure called the revlog. + + + +Efficient storage + +The revlog provides efficient storage of revisions using a +delta mechanism. Instead of storing a complete copy of a file +for each revision, it stores the changes needed to transform an older +revision into the new revision. For many kinds of file data, these +deltas are typically a fraction of a percent of the size of a full +copy of a file. + + +Some obsolete revision control systems can only work with deltas of +text files. They must either store binary files as complete snapshots +or encoded into a text representation, both of which are wasteful +approaches. Mercurial can efficiently handle deltas of files with +arbitrary binary contents; it doesn't need to treat text as special. + + + + +Safe operation +\label{sec:concepts:txn} + + +Mercurial only ever appends data to the end of a revlog file. +It never modifies a section of a file after it has written it. This +is both more robust and efficient than schemes that need to modify or +rewrite data. + + +In addition, Mercurial treats every write as part of a +transaction that can span a number of files. A transaction is +atomic: either the entire transaction succeeds and its effects +are all visible to readers in one go, or the whole thing is undone. +This guarantee of atomicity means that if you're running two copies of +Mercurial, where one is reading data and one is writing it, the reader +will never see a partially written result that might confuse it. + + +The fact that Mercurial only appends to files makes it easier to +provide this transactional guarantee. The easier it is to do stuff +like this, the more confident you should be that it's done correctly. + + + + +Fast retrieval + +Mercurial cleverly avoids a pitfall common to all earlier +revision control systems: the problem of inefficient retrieval. +Most revision control systems store the contents of a revision as an +incremental series of modifications against a snapshot. To +reconstruct a specific revision, you must first read the snapshot, and +then every one of the revisions between the snapshot and your target +revision. The more history that a file accumulates, the more +revisions you must read, hence the longer it takes to reconstruct a +particular revision. + + + + + XXX add text + Snapshot of a revlog, with incremental deltas + \label{fig:concepts:snapshot} + + + +The innovation that Mercurial applies to this problem is simple but +effective. Once the cumulative amount of delta information stored +since the last snapshot exceeds a fixed threshold, it stores a new +snapshot (compressed, of course), instead of another delta. This +makes it possible to reconstruct any revision of a file +quickly. This approach works so well that it has since been copied by +several other revision control systems. + + +Figure illustrates the idea. In an entry +in a revlog's index file, Mercurial stores the range of entries from +the data file that it must read to reconstruct a particular revision. + + + +Aside: the influence of video compression + +If you're familiar with video compression or have ever watched a TV +feed through a digital cable or satellite service, you may know that +most video compression schemes store each frame of video as a delta +against its predecessor frame. In addition, these schemes use +lossy compression techniques to increase the compression ratio, so +visual errors accumulate over the course of a number of inter-frame +deltas. + + +Because it's possible for a video stream to drop out occasionally +due to signal glitches, and to limit the accumulation of artefacts +introduced by the lossy compression process, video encoders +periodically insert a complete frame (called a key frame) into the +video stream; the next delta is generated against that frame. This +means that if the video signal gets interrupted, it will resume once +the next key frame is received. Also, the accumulation of encoding +errors restarts anew with each key frame. + + + + + +Identification and strong integrity + +Along with delta or snapshot information, a revlog entry contains a +cryptographic hash of the data that it represents. This makes it +difficult to forge the contents of a revision, and easy to detect +accidental corruption. + + +Hashes provide more than a mere check against corruption; they are +used as the identifiers for revisions. The changeset identification +hashes that you see as an end user are from revisions of the +changelog. Although filelogs and the manifest also use hashes, +Mercurial only uses these behind the scenes. + + +Mercurial verifies that hashes are correct when it retrieves file +revisions and when it pulls changes from another repository. If it +encounters an integrity problem, it will complain and stop whatever +it's doing. + + +In addition to the effect it has on retrieval efficiency, Mercurial's +use of periodic snapshots makes it more robust against partial data +corruption. If a revlog becomes partly corrupted due to a hardware +error or system bug, it's often possible to reconstruct some or most +revisions from the uncorrupted sections of the revlog, both before and +after the corrupted section. This would not be possible with a +delta-only storage model. + + +\section{Revision history, branching, + and merging} + + +Every entry in a Mercurial revlog knows the identity of its immediate +ancestor revision, usually referred to as its parent. In fact, +a revision contains room for not one parent, but two. Mercurial uses +a special hash, called the null ID, to represent the idea there +is no parent here. This hash is simply a string of zeroes. + + +In figure , you can see an example of the +conceptual structure of a revlog. Filelogs, manifests, and changelogs +all have this same structure; they differ only in the kind of data +stored in each delta or snapshot. + + +The first revision in a revlog (at the bottom of the image) has the +null ID in both of its parent slots. For a normal revision, its +first parent slot contains the ID of its parent revision, and its +second contains the null ID, indicating that the revision has only one +real parent. Any two revisions that have the same parent ID are +branches. A revision that represents a merge between branches has two +normal revision IDs in its parent slots. + + + + + XXX add text + \caption{} + \label{fig:concepts:revlog} + + + + + + +The working directory + +In the working directory, Mercurial stores a snapshot of the files +from the repository as of a particular changeset. + + +The working directory knows which changeset it contains. When you +update the working directory to contain a particular changeset, +Mercurial looks up the appropriate revision of the manifest to find +out which files it was tracking at the time that changeset was +committed, and which revision of each file was then current. It then +recreates a copy of each of those files, with the same contents it had +when the changeset was committed. + + +The dirstate contains Mercurial's knowledge of the working +directory. This details which changeset the working directory is +updated to, and all of the files that Mercurial is tracking in the +working directory. + + +Just as a revision of a revlog has room for two parents, so that it +can represent either a normal revision (with one parent) or a merge of +two earlier revisions, the dirstate has slots for two parents. When +you use the hg update command, the changeset that you update to +is stored in the first parent slot, and the null ID in the second. +When you hg merge with another changeset, the first parent +remains unchanged, and the second parent is filled in with the +changeset you're merging with. The hg parents command tells you +what the parents of the dirstate are. + + + +What happens when you commit + +The dirstate stores parent information for more than just book-keeping +purposes. Mercurial uses the parents of the dirstate as \emph{the + parents of a new changeset} when you perform a commit. + + + + + XXX add text + The working directory can have two parents + \label{fig:concepts:wdir} + + + +Figure shows the normal state of the working +directory, where it has a single changeset as parent. That changeset +is the tip, the newest changeset in the repository that has no +children. + + + + + XXX add text + The working directory gains new parents after a commit + \label{fig:concepts:wdir-after-commit} + + + +It's useful to think of the working directory as the changeset I'm +about to commit. Any files that you tell Mercurial that you've +added, removed, renamed, or copied will be reflected in that +changeset, as will modifications to any files that Mercurial is +already tracking; the new changeset will have the parents of the +working directory as its parents. + + +After a commit, Mercurial will update the parents of the working +directory, so that the first parent is the ID of the new changeset, +and the second is the null ID. This is shown in +figure . Mercurial doesn't touch +any of the files in the working directory when you commit; it just +modifies the dirstate to note its new parents. + + + + +Creating a new head + +It's perfectly normal to update the working directory to a changeset +other than the current tip. For example, you might want to know what +your project looked like last Tuesday, or you could be looking through +changesets to see which one introduced a bug. In cases like this, the +natural thing to do is update the working directory to the changeset +you're interested in, and then examine the files in the working +directory directly to see their contents as they were when you +committed that changeset. The effect of this is shown in +figure . + + + + + XXX add text + The working directory, updated to an older changeset + \label{fig:concepts:wdir-pre-branch} + + + +Having updated the working directory to an older changeset, what +happens if you make some changes, and then commit? Mercurial behaves +in the same way as I outlined above. The parents of the working +directory become the parents of the new changeset. This new changeset +has no children, so it becomes the new tip. And the repository now +contains two changesets that have no children; we call these +heads. You can see the structure that this creates in +figure . + + + + + XXX add text + After a commit made while synced to an older changeset + \label{fig:concepts:wdir-branch} + + + + + If you're new to Mercurial, you should keep in mind a common + error, which is to use the hg pull command without any + options. By default, the hg pull command does not + update the working directory, so you'll bring new changesets into + your repository, but the working directory will stay synced at the + same changeset as before the pull. If you make some changes and + commit afterwards, you'll thus create a new head, because your + working directory isn't synced to whatever the current tip is. + + + I put the word error in quotes because all that you need to do + to rectify this situation is hg merge, then hg commit. In + other words, this almost never has negative consequences; it just + surprises people. I'll discuss other ways to avoid this behaviour, + and why Mercurial behaves in this initially surprising way, later + on. + + + + + +Merging heads + +When you run the hg merge command, Mercurial leaves the first +parent of the working directory unchanged, and sets the second parent +to the changeset you're merging with, as shown in +figure . + + + + + XXX add text + Merging two heads + \label{fig:concepts:wdir-merge} + + + +Mercurial also has to modify the working directory, to merge the files +managed in the two changesets. Simplified a little, the merging +process goes like this, for every file in the manifests of both +changesets. + + +If neither changeset has modified a file, do nothing with that + file. + + +If one changeset has modified a file, and the other hasn't, + create the modified copy of the file in the working directory. + + +If one changeset has removed a file, and the other hasn't (or + has also deleted it), delete the file from the working directory. + + +If one changeset has removed a file, but the other has modified + the file, ask the user what to do: keep the modified file, or remove + it? + + +If both changesets have modified a file, invoke an external + merge program to choose the new contents for the merged file. This + may require input from the user. + + +If one changeset has modified a file, and the other has renamed + or copied the file, make sure that the changes follow the new name + of the file. + + +There are more details&emdash;merging has plenty of corner cases&emdash;but +these are the most common choices that are involved in a merge. As +you can see, most cases are completely automatic, and indeed most +merges finish automatically, without requiring your input to resolve +any conflicts. + + +When you're thinking about what happens when you commit after a merge, +once again the working directory is the changeset I'm about to +commit. After the hg merge command completes, the working +directory has two parents; these will become the parents of the new +changeset. + + +Mercurial lets you perform multiple merges, but you must commit the +results of each individual merge as you go. This is necessary because +Mercurial only tracks two parents for both revisions and the working +directory. While it would be technically possible to merge multiple +changesets at once, the prospect of user confusion and making a +terrible mess of a merge immediately becomes overwhelming. + + + + + +Other interesting design features + +In the sections above, I've tried to highlight some of the most +important aspects of Mercurial's design, to illustrate that it pays +careful attention to reliability and performance. However, the +attention to detail doesn't stop there. There are a number of other +aspects of Mercurial's construction that I personally find +interesting. I'll detail a few of them here, separate from the big +ticket items above, so that if you're interested, you can gain a +better idea of the amount of thinking that goes into a well-designed +system. + + + +Clever compression + +When appropriate, Mercurial will store both snapshots and deltas in +compressed form. It does this by always trying to compress a +snapshot or delta, but only storing the compressed version if it's +smaller than the uncompressed version. + + +This means that Mercurial does the right thing when storing a file +whose native form is compressed, such as a zip archive or a +JPEG image. When these types of files are compressed a second time, +the resulting file is usually bigger than the once-compressed form, +and so Mercurial will store the plain zip or JPEG. + + +Deltas between revisions of a compressed file are usually larger than +snapshots of the file, and Mercurial again does the right thing in +these cases. It finds that such a delta exceeds the threshold at +which it should store a complete snapshot of the file, so it stores +the snapshot, again saving space compared to a naive delta-only +approach. + + + +Network recompression + +When storing revisions on disk, Mercurial uses the deflate +compression algorithm (the same one used by the popular zip +archive format), which balances good speed with a respectable +compression ratio. However, when transmitting revision data over a +network connection, Mercurial uncompresses the compressed revision +data. + + +If the connection is over HTTP, Mercurial recompresses the entire +stream of data using a compression algorithm that gives a better +compression ratio (the Burrows-Wheeler algorithm from the widely used +bzip2 compression package). This combination of algorithm +and compression of the entire stream (instead of a revision at a time) +substantially reduces the number of bytes to be transferred, yielding +better network performance over almost all kinds of network. + + +(If the connection is over ssh, Mercurial doesn't +recompress the stream, because ssh can already do this +itself.) + + + + + +Read/write ordering and atomicity + +Appending to files isn't the whole story when it comes to guaranteeing +that a reader won't see a partial write. If you recall +figure , revisions in the changelog point to +revisions in the manifest, and revisions in the manifest point to +revisions in filelogs. This hierarchy is deliberate. + + +A writer starts a transaction by writing filelog and manifest data, +and doesn't write any changelog data until those are finished. A +reader starts by reading changelog data, then manifest data, followed +by filelog data. + + +Since the writer has always finished writing filelog and manifest data +before it writes to the changelog, a reader will never read a pointer +to a partially written manifest revision from the changelog, and it will +never read a pointer to a partially written filelog revision from the +manifest. + + + + +Concurrent access + +The read/write ordering and atomicity guarantees mean that Mercurial +never needs to lock a repository when it's reading data, even +if the repository is being written to while the read is occurring. +This has a big effect on scalability; you can have an arbitrary number +of Mercurial processes safely reading data from a repository safely +all at once, no matter whether it's being written to or not. + + +The lockless nature of reading means that if you're sharing a +repository on a multi-user system, you don't need to grant other local +users permission to write to your repository in order for them +to be able to clone it or pull changes from it; they only need +read permission. (This is not a common feature among +revision control systems, so don't take it for granted! Most require +readers to be able to lock a repository to access it safely, and this +requires write permission on at least one directory, which of course +makes for all kinds of nasty and annoying security and administrative +problems.) + + +Mercurial uses locks to ensure that only one process can write to a +repository at a time (the locking mechanism is safe even over +filesystems that are notoriously hostile to locking, such as NFS). If +a repository is locked, a writer will wait for a while to retry if the +repository becomes unlocked, but if the repository remains locked for +too long, the process attempting to write will time out after a while. +This means that your daily automated scripts won't get stuck forever +and pile up if a system crashes unnoticed, for example. (Yes, the +timeout is configurable, from zero to infinity.) + + + +Safe dirstate access + +As with revision data, Mercurial doesn't take a lock to read the +dirstate file; it does acquire a lock to write it. To avoid the +possibility of reading a partially written copy of the dirstate file, +Mercurial writes to a file with a unique name in the same directory as +the dirstate file, then renames the temporary file atomically to +dirstate. The file named dirstate is thus +guaranteed to be complete, not partially written. + + + + + +Avoiding seeks + +Critical to Mercurial's performance is the avoidance of seeks of the +disk head, since any seek is far more expensive than even a +comparatively large read operation. + + +This is why, for example, the dirstate is stored in a single file. If +there were a dirstate file per directory that Mercurial tracked, the +disk would seek once per directory. Instead, Mercurial reads the +entire single dirstate file in one step. + + +Mercurial also uses a copy on write scheme when cloning a +repository on local storage. Instead of copying every revlog file +from the old repository into the new repository, it makes a hard +link, which is a shorthand way to say these two names point to the +same file. When Mercurial is about to write to one of a revlog's +files, it checks to see if the number of names pointing at the file is +greater than one. If it is, more than one repository is using the +file, so Mercurial makes a new copy of the file that is private to +this repository. + + +A few revision control developers have pointed out that this idea of +making a complete private copy of a file is not very efficient in its +use of storage. While this is true, storage is cheap, and this method +gives the highest performance while deferring most book-keeping to the +operating system. An alternative scheme would most likely reduce +performance and increase the complexity of the software, each of which +is much more important to the feel of day-to-day use. + + + + +Other contents of the dirstate + +Because Mercurial doesn't force you to tell it when you're modifying a +file, it uses the dirstate to store some extra information so it can +determine efficiently whether you have modified a file. For each file +in the working directory, it stores the time that it last modified the +file itself, and the size of the file at that time. + + +When you explicitly hg add, hg remove, hg rename or +hg copy files, Mercurial updates the dirstate so that it knows +what to do with those files when you commit. + + +When Mercurial is checking the states of files in the working +directory, it first checks a file's modification time. If that has +not changed, the file must not have been modified. If the file's size +has changed, the file must have been modified. If the modification +time has changed, but the size has not, only then does Mercurial need +to read the actual contents of the file to see if they've changed. +Storing these few extra pieces of information dramatically reduces the +amount of data that Mercurial needs to read, which yields large +performance improvements compared to other revision control systems. + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch05-daily.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch05-daily.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,474 @@ + + + +Mercurial in daily use +\label{chap:daily} + + +Telling Mercurial which files to track + +Mercurial does not work with files in your repository unless you tell +it to manage them. The hg status command will tell you which +files Mercurial doesn't know about; it uses a ? to +display such files. + +To tell Mercurial to track a file, use the hg add command. Once +you have added a file, the entry in the output of hg status for +that file changes from ? to A. + + +After you run a hg commit, the files that you added before the +commit will no longer be listed in the output of hg status. The +reason for this is that hg status only tells you about +interesting files&emdash;those that you have modified or told Mercurial +to do something with&emdash;by default. If you have a repository that +contains thousands of files, you will rarely want to know about files +that Mercurial is tracking, but that have not changed. (You can still +get this information; we'll return to this later.) + +Once you add a file, Mercurial doesn't do anything with it +immediately. Instead, it will take a snapshot of the file's state the +next time you perform a commit. It will then continue to track the +changes you make to the file every time you commit, until you remove +the file. + + +Explicit versus implicit file naming + +A useful behaviour that Mercurial has is that if you pass the name of +a directory to a command, every Mercurial command will treat this as +I want to operate on every file in this directory and its +subdirectories. + +Notice in this example that Mercurial printed the names of the files +it added, whereas it didn't do so when we added the file named +a in the earlier example. + +What's going on is that in the former case, we explicitly named the +file to add on the command line, so the assumption that Mercurial +makes in such cases is that you know what you were doing, and it +doesn't print any output. + +However, when we imply the names of files by giving the name of +a directory, Mercurial takes the extra step of printing the name of +each file that it does something with. This makes it more clear what +is happening, and reduces the likelihood of a silent and nasty +surprise. This behaviour is common to most Mercurial commands. + + + +Aside: Mercurial tracks files, not directories + +Mercurial does not track directory information. Instead, it tracks +the path to a file. Before creating a file, it first creates any +missing directory components of the path. After it deletes a file, it +then deletes any empty directories that were in the deleted file's +path. This sounds like a trivial distinction, but it has one minor +practical consequence: it is not possible to represent a completely +empty directory in Mercurial. + + +Empty directories are rarely useful, and there are unintrusive +workarounds that you can use to achieve an appropriate effect. The +developers of Mercurial thus felt that the complexity that would be +required to manage empty directories was not worth the limited benefit +this feature would bring. + + +If you need an empty directory in your repository, there are a few +ways to achieve this. One is to create a directory, then hg add a +hidden file to that directory. On Unix-like systems, any file +name that begins with a period (.) is treated as hidden +by most commands and GUI tools. This approach is illustrated in +figure . + + + + + Simulating an empty directory using a hidden file + \label{ex:daily:hidden} + + + +Another way to tackle a need for an empty directory is to simply +create one in your automated build scripts before they will need it. + + + + + +How to stop tracking a file + +Once you decide that a file no longer belongs in your repository, use +the hg remove command; this deletes the file, and tells Mercurial +to stop tracking it. A removed file is represented in the output of +hg status with a R. + + + +After you hg remove a file, Mercurial will no longer track +changes to that file, even if you recreate a file with the same name +in your working directory. If you do recreate a file with the same +name and want Mercurial to track the new file, simply hg add it. +Mercurial will know that the newly added file is not related to the +old file of the same name. + + + +Removing a file does not affect its history + +It is important to understand that removing a file has only two +effects. + + +It removes the current version of the file from the working + directory. + + +It stops Mercurial from tracking changes to the file, from the + time of the next commit. + + +Removing a file does not in any way alter the history of +the file. + + +If you update the working directory to a changeset in which a file +that you have removed was still tracked, it will reappear in the +working directory, with the contents it had when you committed that +changeset. If you then update the working directory to a later +changeset, in which the file had been removed, Mercurial will once +again remove the file from the working directory. + + + + +Missing files + +Mercurial considers a file that you have deleted, but not used +hg remove to delete, to be missing. A missing file is +represented with ! in the output of hg status. +Mercurial commands will not generally do anything with missing files. + + + +If your repository contains a file that hg status reports as +missing, and you want the file to stay gone, you can run +hg remove at any time later on, to +tell Mercurial that you really did mean to remove the file. + + + +On the other hand, if you deleted the missing file by accident, use +hg revert filename to recover the file. It will +reappear, in unmodified form. + + + +\subsection{Aside: why tell Mercurial explicitly to + remove a file?} + + +You might wonder why Mercurial requires you to explicitly tell it that +you are deleting a file. Early during the development of Mercurial, +it let you delete a file however you pleased; Mercurial would notice +the absence of the file automatically when you next ran a +hg commit, and stop tracking the file. In practice, this made it +too easy to accidentally remove a file without noticing. + + +\subsection{Useful shorthand&emdash;adding and removing files + in one step} + + +Mercurial offers a combination command, hg addremove, that adds +untracked files and marks missing files as removed. + +The hg commit command also provides a option +that performs this same add-and-remove, immediately followed by a +commit. + + + + + + +Copying files + +Mercurial provides a hg copy command that lets you make a new +copy of a file. When you copy a file using this command, Mercurial +makes a record of the fact that the new file is a copy of the original +file. It treats these copied files specially when you merge your work +with someone else's. + + + +The results of copying during a merge + +What happens during a merge is that changes follow a copy. To +best illustrate what this means, let's create an example. We'll start +with the usual tiny repository that contains a single file. + +We need to do some work in parallel, so that we'll have something to +merge. So let's clone our repository. + +Back in our initial repository, let's use the hg copy command to +make a copy of the first file we created. + + + +If we look at the output of the hg status command afterwards, the +copied file looks just like a normal added file. + +But if we pass the option to hg status, it +prints another line of output: this is the file that our newly-added +file was copied from. + + + +Now, back in the repository we cloned, let's make a change in +parallel. We'll add a line of content to the original file that we +created. + +Now we have a modified file in this repository. When we +pull the changes from the first repository, and merge the two heads, +Mercurial will propagate the changes that we made locally to +file into its copy, new-file. + + + + + +Why should changes follow copies? +\label{sec:daily:why-copy} + + +This behaviour, of changes to a file propagating out to copies of the +file, might seem esoteric, but in most cases it's highly desirable. + + +First of all, remember that this propagation only happens when +you merge. So if you hg copy a file, and subsequently modify the +original file during the normal course of your work, nothing will +happen. + + +The second thing to know is that modifications will only propagate +across a copy as long as the repository that you're pulling changes +from doesn't know about the copy. + + +The reason that Mercurial does this is as follows. Let's say I make +an important bug fix in a source file, and commit my changes. +Meanwhile, you've decided to hg copy the file in your repository, +without knowing about the bug or having seen the fix, and you have +started hacking on your copy of the file. + + +If you pulled and merged my changes, and Mercurial didn't +propagate changes across copies, your source file would now contain +the bug, and unless you remembered to propagate the bug fix by hand, +the bug would remain in your copy of the file. + + +By automatically propagating the change that fixed the bug from the +original file to the copy, Mercurial prevents this class of problem. +To my knowledge, Mercurial is the only revision control system +that propagates changes across copies like this. + + +Once your change history has a record that the copy and subsequent +merge occurred, there's usually no further need to propagate changes +from the original file to the copied file, and that's why Mercurial +only propagates changes across copies until this point, and no +further. + + + + +How to make changes <emphasis>not</emphasis> follow a copy + +If, for some reason, you decide that this business of automatically +propagating changes across copies is not for you, simply use your +system's normal file copy command (on Unix-like systems, that's +cp) to make a copy of a file, then hg add the new copy +by hand. Before you do so, though, please do reread +section , and make an informed decision that +this behaviour is not appropriate to your specific case. + + + + +Behaviour of the <command role="hg-cmd">hg copy</command> command + +When you use the hg copy command, Mercurial makes a copy of each +source file as it currently stands in the working directory. This +means that if you make some modifications to a file, then hg copy +it without first having committed those changes, the new copy will +also contain the modifications you have made up until that point. (I +find this behaviour a little counterintuitive, which is why I mention +it here.) + + +The hg copy command acts similarly to the Unix cp +command (you can use the hg cp alias if you prefer). The last +argument is the destination, and all prior arguments are +sources. If you pass it a single file as the source, and the +destination does not exist, it creates a new file with that name. + +If the destination is a directory, Mercurial copies its sources into +that directory. + +Copying a directory is recursive, and preserves the directory +structure of the source. + +If the source and destination are both directories, the source tree is +recreated in the destination directory. + + + +As with the hg rename command, if you copy a file manually and +then want Mercurial to know that you've copied the file, simply use +the option to hg copy. + + + + + + +Renaming files + +It's rather more common to need to rename a file than to make a copy +of it. The reason I discussed the hg copy command before talking +about renaming files is that Mercurial treats a rename in essentially +the same way as a copy. Therefore, knowing what Mercurial does when +you copy a file tells you what to expect when you rename a file. + + +When you use the hg rename command, Mercurial makes a copy of +each source file, then deletes it and marks the file as removed. + +The hg status command shows the newly copied file as added, and +the copied-from file as removed. + +As with the results of a hg copy, we must use the + option to hg status to see that the added file +is really being tracked by Mercurial as a copy of the original, now +removed, file. + + + +As with hg remove and hg copy, you can tell Mercurial about +a rename after the fact using the option. In +most other respects, the behaviour of the hg rename command, and +the options it accepts, are similar to the hg copy command. + + + +Renaming files and merging changes + +Since Mercurial's rename is implemented as copy-and-remove, the same +propagation of changes happens when you merge after a rename as after +a copy. + + +If I modify a file, and you rename it to a new name, and then we merge +our respective changes, my modifications to the file under its +original name will be propagated into the file under its new name. +(This is something you might expect to simply work, but not all +revision control systems actually do this.) + + +Whereas having changes follow a copy is a feature where you can +perhaps nod and say yes, that might be useful, it should be clear +that having them follow a rename is definitely important. Without +this facility, it would simply be too easy for changes to become +orphaned when files are renamed. + + + + +Divergent renames and merging + +The case of diverging names occurs when two developers start with a +file&emdash;let's call it foo&emdash;in their respective +repositories. + + + +Anne renames the file to bar. + +Meanwhile, Bob renames it to quux. + + + +I like to think of this as a conflict because each developer has +expressed different intentions about what the file ought to be named. + + +What do you think should happen when they merge their work? +Mercurial's actual behaviour is that it always preserves both +names when it merges changesets that contain divergent renames. + + + +Notice that Mercurial does warn about the divergent renames, but it +leaves it up to you to do something about the divergence after the merge. + + + + +Convergent renames and merging + +Another kind of rename conflict occurs when two people choose to +rename different source files to the same destination. +In this case, Mercurial runs its normal merge machinery, and lets you +guide it to a suitable resolution. + + + + +Other name-related corner cases + +Mercurial has a longstanding bug in which it fails to handle a merge +where one side has a file with a given name, while another has a +directory with the same name. This is documented as issue 29. + + + + + + +Recovering from mistakes + +Mercurial has some useful commands that will help you to recover from +some common mistakes. + + +The hg revert command lets you undo changes that you have made to +your working directory. For example, if you hg add a file by +accident, just run hg revert with the name of the file you added, +and while the file won't be touched in any way, it won't be tracked +for adding by Mercurial any longer, either. You can also use +hg revert to get rid of erroneous changes to a file. + + +It's useful to remember that the hg revert command is useful for +changes that you have not yet committed. Once you've committed a +change, if you decide it was a mistake, you can still do something +about it, though your options may be more limited. + + +For more information about the hg revert command, and details +about how to deal with changes you have already committed, see +chapter . + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch06-collab.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch06-collab.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,1405 @@ + + + +Collaborating with other people +\label{cha:collab} + +As a completely decentralised tool, Mercurial doesn't impose any +policy on how people ought to work with each other. However, if +you're new to distributed revision control, it helps to have some +tools and examples in mind when you're thinking about possible +workflow models. + + +Mercurial's web interface + +Mercurial has a powerful web interface that provides several +useful capabilities. + +For interactive use, the web interface lets you browse a single +repository or a collection of repositories. You can view the history +of a repository, examine each change (comments and diffs), and view +the contents of each directory and file. + +Also for human consumption, the web interface provides an RSS feed of +the changes in a repository. This lets you subscribe to a +repository using your favourite feed reader, and be automatically +notified of activity in that repository as soon as it happens. I find +this capability much more convenient than the model of subscribing to +a mailing list to which notifications are sent, as it requires no +additional configuration on the part of whoever is serving the +repository. + +The web interface also lets remote users clone a repository, pull +changes from it, and (when the server is configured to permit it) push +changes back to it. Mercurial's HTTP tunneling protocol aggressively +compresses data, so that it works efficiently even over low-bandwidth +network connections. + +The easiest way to get started with the web interface is to use your +web browser to visit an existing repository, such as the master +Mercurial repository at +http://www.selenic.com/repo/hg?style=gitweb. + +If you're interested in providing a web interface to your own +repositories, Mercurial provides two ways to do this. The first is +using the hg serve command, which is best suited to short-term +lightweight serving. See section below for +details of how to use this command. If you have a long-lived +repository that you'd like to make permanently available, Mercurial +has built-in support for the CGI (Common Gateway Interface) standard, +which all common web servers support. See +section for details of CGI configuration. + + + +Collaboration models + +With a suitably flexible tool, making decisions about workflow is much +more of a social engineering challenge than a technical one. +Mercurial imposes few limitations on how you can structure the flow of +work in a project, so it's up to you and your group to set up and live +with a model that matches your own particular needs. + + + +Factors to keep in mind + +The most important aspect of any model that you must keep in mind is +how well it matches the needs and capabilities of the people who will +be using it. This might seem self-evident; even so, you still can't +afford to forget it for a moment. + + +I once put together a workflow model that seemed to make perfect sense +to me, but that caused a considerable amount of consternation and +strife within my development team. In spite of my attempts to explain +why we needed a complex set of branches, and how changes ought to flow +between them, a few team members revolted. Even though they were +smart people, they didn't want to pay attention to the constraints we +were operating under, or face the consequences of those constraints in +the details of the model that I was advocating. + + +Don't sweep foreseeable social or technical problems under the rug. +Whatever scheme you put into effect, you should plan for mistakes and +problem scenarios. Consider adding automated machinery to prevent, or +quickly recover from, trouble that you can anticipate. As an example, +if you intend to have a branch with not-for-release changes in it, +you'd do well to think early about the possibility that someone might +accidentally merge those changes into a release branch. You could +avoid this particular problem by writing a hook that prevents changes +from being merged from an inappropriate branch. + + + + +Informal anarchy + +I wouldn't suggest an anything goes approach as something +sustainable, but it's a model that's easy to grasp, and it works +perfectly well in a few unusual situations. + + +As one example, many projects have a loose-knit group of collaborators +who rarely physically meet each other. Some groups like to overcome +the isolation of working at a distance by organising occasional +sprints. In a sprint, a number of people get together in a single +location (a company's conference room, a hotel meeting room, that kind +of place) and spend several days more or less locked in there, hacking +intensely on a handful of projects. + + +A sprint is the perfect place to use the hg serve command, since +hg serve does not requires any fancy server infrastructure. You +can get started with hg serve in moments, by reading +section below. Then simply tell the person +next to you that you're running a server, send the URL to them in an +instant message, and you immediately have a quick-turnaround way to +work together. They can type your URL into their web browser and +quickly review your changes; or they can pull a bugfix from you and +verify it; or they can clone a branch containing a new feature and try +it out. + + +The charm, and the problem, with doing things in an ad hoc fashion +like this is that only people who know about your changes, and where +they are, can see them. Such an informal approach simply doesn't +scale beyond a handful people, because each individual needs to know +about $n$ different repositories to pull from. + + + + +A single central repository + +For smaller projects migrating from a centralised revision control +tool, perhaps the easiest way to get started is to have changes flow +through a single shared central repository. This is also the +most common building block for more ambitious workflow schemes. + + +Contributors start by cloning a copy of this repository. They can +pull changes from it whenever they need to, and some (perhaps all) +developers have permission to push a change back when they're ready +for other people to see it. + + +Under this model, it can still often make sense for people to pull +changes directly from each other, without going through the central +repository. Consider a case in which I have a tentative bug fix, but +I am worried that if I were to publish it to the central repository, +it might subsequently break everyone else's trees as they pull it. To +reduce the potential for damage, I can ask you to clone my repository +into a temporary repository of your own and test it. This lets us put +off publishing the potentially unsafe change until it has had a little +testing. + + +In this kind of scenario, people usually use the ssh +protocol to securely push changes to the central repository, as +documented in section . It's also usual to +publish a read-only copy of the repository over HTTP using CGI, as in +section . Publishing over HTTP satisfies the +needs of people who don't have push access, and those who want to use +web browsers to browse the repository's history. + + + + +Working with multiple branches + +Projects of any significant size naturally tend to make progress on +several fronts simultaneously. In the case of software, it's common +for a project to go through periodic official releases. A release +might then go into maintenance mode for a while after its first +publication; maintenance releases tend to contain only bug fixes, not +new features. In parallel with these maintenance releases, one or +more future releases may be under development. People normally use +the word branch to refer to one of these many slightly different +directions in which development is proceeding. + + +Mercurial is particularly well suited to managing a number of +simultaneous, but not identical, branches. Each development +direction can live in its own central repository, and you can merge +changes from one to another as the need arises. Because repositories +are independent of each other, unstable changes in a development +branch will never affect a stable branch unless someone explicitly +merges those changes in. + + +Here's an example of how this can work in practice. Let's say you +have one main branch on a central server. + +People clone it, make changes locally, test them, and push them back. + + +Once the main branch reaches a release milestone, you can use the +hg tag command to give a permanent name to the milestone +revision. + +Let's say some ongoing development occurs on the main branch. + +Using the tag that was recorded at the milestone, people who clone +that repository at any time in the future can use hg update to +get a copy of the working directory exactly as it was when that tagged +revision was committed. + + + +In addition, immediately after the main branch is tagged, someone can +then clone the main branch on the server to a new stable branch, +also on the server. + + + +Someone who needs to make a change to the stable branch can then clone +that repository, make their changes, commit, and push their +changes back there. + +Because Mercurial repositories are independent, and Mercurial doesn't +move changes around automatically, the stable and main branches are +isolated from each other. The changes that you made on the +main branch don't leak to the stable branch, and vice versa. + + +You'll often want all of your bugfixes on the stable branch to show up +on the main branch, too. Rather than rewrite a bugfix on the main +branch, you can simply pull and merge changes from the stable to the +main branch, and Mercurial will bring those bugfixes in for you. + +The main branch will still contain changes that are not on the stable +branch, but it will also contain all of the bugfixes from the stable +branch. The stable branch remains unaffected by these changes. + + + + +Feature branches + +For larger projects, an effective way to manage change is to break up +a team into smaller groups. Each group has a shared branch of its +own, cloned from a single master branch used by the entire +project. People working on an individual branch are typically quite +isolated from developments on other branches. + + + + + XXX add text + Feature branches + \label{fig:collab:feature-branches} + + + +When a particular feature is deemed to be in suitable shape, someone +on that feature team pulls and merges from the master branch into the +feature branch, then pushes back up to the master branch. + + + + +The release train + +Some projects are organised on a train basis: a release is +scheduled to happen every few months, and whatever features are ready +when the train is ready to leave are allowed in. + + +This model resembles working with feature branches. The difference is +that when a feature branch misses a train, someone on the feature team +pulls and merges the changes that went out on that train release into +the feature branch, and the team continues its work on top of that +release so that their feature can make the next release. + + + + +The Linux kernel model + +The development of the Linux kernel has a shallow hierarchical +structure, surrounded by a cloud of apparent chaos. Because most +Linux developers use git, a distributed revision control +tool with capabilities similar to Mercurial, it's useful to describe +the way work flows in that environment; if you like the ideas, the +approach translates well across tools. + + +At the center of the community sits Linus Torvalds, the creator of +Linux. He publishes a single source repository that is considered the +authoritative current tree by the entire developer community. +Anyone can clone Linus's tree, but he is very choosy about whose trees +he pulls from. + + +Linus has a number of trusted lieutenants. As a general rule, he +pulls whatever changes they publish, in most cases without even +reviewing those changes. Some of those lieutenants are generally +agreed to be maintainers, responsible for specific subsystems +within the kernel. If a random kernel hacker wants to make a change +to a subsystem that they want to end up in Linus's tree, they must +find out who the subsystem's maintainer is, and ask that maintainer to +take their change. If the maintainer reviews their changes and agrees +to take them, they'll pass them along to Linus in due course. + + +Individual lieutenants have their own approaches to reviewing, +accepting, and publishing changes; and for deciding when to feed them +to Linus. In addition, there are several well known branches that +people use for different purposes. For example, a few people maintain +stable repositories of older versions of the kernel, to which they +apply critical fixes as needed. Some maintainers publish multiple +trees: one for experimental changes; one for changes that they are +about to feed upstream; and so on. Others just publish a single +tree. + + +This model has two notable features. The first is that it's pull +only. You have to ask, convince, or beg another developer to take a +change from you, because there are almost no trees to which more than +one person can push, and there's no way to push changes into a tree +that someone else controls. + + +The second is that it's based on reputation and acclaim. If you're an +unknown, Linus will probably ignore changes from you without even +responding. But a subsystem maintainer will probably review them, and +will likely take them if they pass their criteria for suitability. +The more good changes you contribute to a maintainer, the more +likely they are to trust your judgment and accept your changes. If +you're well-known and maintain a long-lived branch for something Linus +hasn't yet accepted, people with similar interests may pull your +changes regularly to keep up with your work. + + +Reputation and acclaim don't necessarily cross subsystem or people +boundaries. If you're a respected but specialised storage hacker, and +you try to fix a networking bug, that change will receive a level of +scrutiny from a network maintainer comparable to a change from a +complete stranger. + + +To people who come from more orderly project backgrounds, the +comparatively chaotic Linux kernel development process often seems +completely insane. It's subject to the whims of individuals; people +make sweeping changes whenever they deem it appropriate; and the pace +of development is astounding. And yet Linux is a highly successful, +well-regarded piece of software. + + + + +Pull-only versus shared-push collaboration + +A perpetual source of heat in the open source community is whether a +development model in which people only ever pull changes from others +is better than one in which multiple people can push changes to a +shared repository. + + +Typically, the backers of the shared-push model use tools that +actively enforce this approach. If you're using a centralised +revision control tool such as Subversion, there's no way to make a +choice over which model you'll use: the tool gives you shared-push, +and if you want to do anything else, you'll have to roll your own +approach on top (such as applying a patch by hand). + + +A good distributed revision control tool, such as Mercurial, will +support both models. You and your collaborators can then structure +how you work together based on your own needs and preferences, not on +what contortions your tools force you into. + + + + +Where collaboration meets branch management + +Once you and your team set up some shared repositories and start +propagating changes back and forth between local and shared repos, you +begin to face a related, but slightly different challenge: that of +managing the multiple directions in which your team may be moving at +once. Even though this subject is intimately related to how your team +collaborates, it's dense enough to merit treatment of its own, in +chapter . + + + + + +The technical side of sharing + +The remainder of this chapter is devoted to the question of serving +data to your collaborators. + + + + +Informal sharing with <command role="hg-cmd">hg serve</command> +\label{sec:collab:serve} + + +Mercurial's hg serve command is wonderfully suited to small, +tight-knit, and fast-paced group environments. It also provides a +great way to get a feel for using Mercurial commands over a network. + + +Run hg serve inside a repository, and in under a second it will +bring up a specialised HTTP server; this will accept connections from +any client, and serve up data for that repository until you terminate +it. Anyone who knows the URL of the server you just started, and can +talk to your computer over the network, can then use a web browser or +Mercurial to read data from that repository. A URL for a +hg serve instance running on a laptop is likely to look something +like http://my-laptop.local:8000/. + + +The hg serve command is not a general-purpose web server. +It can do only two things: + + +Allow people to browse the history of the repository it's + serving, from their normal web browsers. + + +Speak Mercurial's wire protocol, so that people can + hg clone or hg pull changes from that repository. + + +In particular, hg serve won't allow remote users to modify +your repository. It's intended for read-only use. + + +If you're getting started with Mercurial, there's nothing to prevent +you from using hg serve to serve up a repository on your own +computer, then use commands like hg clone, hg incoming, and +so on to talk to that server as if the repository was hosted remotely. +This can help you to quickly get acquainted with using commands on +network-hosted repositories. + + + +A few things to keep in mind + +Because it provides unauthenticated read access to all clients, you +should only use hg serve in an environment where you either don't +care, or have complete control over, who can access your network and +pull data from your repository. + + +The hg serve command knows nothing about any firewall software +you might have installed on your system or network. It cannot detect +or control your firewall software. If other people are unable to talk +to a running hg serve instance, the second thing you should do +(after you make sure that they're using the correct URL) is +check your firewall configuration. + + +By default, hg serve listens for incoming connections on +port 8000. If another process is already listening on the port you +want to use, you can specify a different port to listen on using the + option. + + +Normally, when hg serve starts, it prints no output, which can be +a bit unnerving. If you'd like to confirm that it is indeed running +correctly, and find out what URL you should send to your +collaborators, start it with the option. + + + + + +Using the Secure Shell (ssh) protocol +\label{sec:collab:ssh} + + +You can pull and push changes securely over a network connection using +the Secure Shell (ssh) protocol. To use this successfully, +you may have to do a little bit of configuration on the client or +server sides. + + +If you're not familiar with ssh, it's a network protocol that lets you +securely communicate with another computer. To use it with Mercurial, +you'll be setting up one or more user accounts on a server so that +remote users can log in and execute commands. + + +(If you are familiar with ssh, you'll probably find some of the +material that follows to be elementary in nature.) + + + +How to read and write ssh URLs + +An ssh URL tends to look like this: + + + ssh://bos@hg.serpentine.com:22/hg/hgbook + + + +The ssh:// part tells Mercurial to use the ssh + protocol. + + +The bos@ component indicates what username to log + into the server as. You can leave this out if the remote username + is the same as your local username. + + +The hg.serpentine.com gives the hostname of the + server to log into. + + +The :22 identifies the port number to connect to the server + on. The default port is 22, so you only need to specify this part + if you're not using port 22. + + +The remainder of the URL is the local path to the repository on + the server. + + + +There's plenty of scope for confusion with the path component of ssh +URLs, as there is no standard way for tools to interpret it. Some +programs behave differently than others when dealing with these paths. +This isn't an ideal situation, but it's unlikely to change. Please +read the following paragraphs carefully. + + +Mercurial treats the path to a repository on the server as relative to +the remote user's home directory. For example, if user foo +on the server has a home directory of /home/foo, then an ssh +URL that contains a path component of bar +really refers to the directory /home/foo/bar. + + +If you want to specify a path relative to another user's home +directory, you can use a path that starts with a tilde character +followed by the user's name (let's call them otheruser), like +this. + + + ssh://server/ otheruser/hg/repo + + + +And if you really want to specify an absolute path on the +server, begin the path component with two slashes, as in this example. + + + ssh://server//absolute/path + + + + + +Finding an ssh client for your system + +Almost every Unix-like system comes with OpenSSH preinstalled. If +you're using such a system, run which ssh to find out if +the ssh command is installed (it's usually in +/usr/bin). In the unlikely event that it isn't present, +take a look at your system documentation to figure out how to install +it. + + +On Windows, you'll first need to download a suitable ssh +client. There are two alternatives. + + +Simon Tatham's excellent PuTTY package web:putty provides + a complete suite of ssh client commands. + + +If you have a high tolerance for pain, you can use the Cygwin + port of OpenSSH. + + +In either case, you'll need to edit your \hgini\ file to tell +Mercurial where to find the actual client command. For example, if +you're using PuTTY, you'll need to use the plink command as +a command-line ssh client. + + + [ui] + ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key" + + + + + The path to plink shouldn't contain any whitespace + characters, or Mercurial may not be able to run it correctly (so + putting it in C:\\Program Files is probably not a good + idea). + + + + + +Generating a key pair + +To avoid the need to repetitively type a password every time you need +to use your ssh client, I recommend generating a key pair. On a +Unix-like system, the ssh-keygen command will do the trick. +On Windows, if you're using PuTTY, the puttygen command is +what you'll need. + + +When you generate a key pair, it's usually highly advisable to +protect it with a passphrase. (The only time that you might not want +to do this is when you're using the ssh protocol for automated tasks +on a secure network.) + + +Simply generating a key pair isn't enough, however. You'll need to +add the public key to the set of authorised keys for whatever user +you're logging in remotely as. For servers using OpenSSH (the vast +majority), this will mean adding the public key to a list in a file +called authorized_keys in their .ssh +directory. + + +On a Unix-like system, your public key will have a .pub +extension. If you're using puttygen on Windows, you can +save the public key to a file of your choosing, or paste it from the +window it's displayed in straight into the +authorized_keys file. + + + + +Using an authentication agent + +An authentication agent is a daemon that stores passphrases in memory +(so it will forget passphrases if you log out and log back in again). +An ssh client will notice if it's running, and query it for a +passphrase. If there's no authentication agent running, or the agent +doesn't store the necessary passphrase, you'll have to type your +passphrase every time Mercurial tries to communicate with a server on +your behalf (e.g. whenever you pull or push changes). + + +The downside of storing passphrases in an agent is that it's possible +for a well-prepared attacker to recover the plain text of your +passphrases, in some cases even if your system has been power-cycled. +You should make your own judgment as to whether this is an acceptable +risk. It certainly saves a lot of repeated typing. + + +On Unix-like systems, the agent is called ssh-agent, and +it's often run automatically for you when you log in. You'll need to +use the ssh-add command to add passphrases to the agent's +store. On Windows, if you're using PuTTY, the pageant +command acts as the agent. It adds an icon to your system tray that +will let you manage stored passphrases. + + + + +Configuring the server side properly + +Because ssh can be fiddly to set up if you're new to it, there's a +variety of things that can go wrong. Add Mercurial on top, and +there's plenty more scope for head-scratching. Most of these +potential problems occur on the server side, not the client side. The +good news is that once you've gotten a configuration working, it will +usually continue to work indefinitely. + + +Before you try using Mercurial to talk to an ssh server, it's best to +make sure that you can use the normal ssh or putty +command to talk to the server first. If you run into problems with +using these commands directly, Mercurial surely won't work. Worse, it +will obscure the underlying problem. Any time you want to debug +ssh-related Mercurial problems, you should drop back to making sure +that plain ssh client commands work first, before you worry +about whether there's a problem with Mercurial. + + +The first thing to be sure of on the server side is that you can +actually log in from another machine at all. If you can't use +ssh or putty to log in, the error message you get +may give you a few hints as to what's wrong. The most common problems +are as follows. + + +If you get a connection refused error, either there isn't an + SSH daemon running on the server at all, or it's inaccessible due to + firewall configuration. + + +If you get a no route to host error, you either have an + incorrect address for the server or a seriously locked down firewall + that won't admit its existence at all. + + +If you get a permission denied error, you may have mistyped + the username on the server, or you could have mistyped your key's + passphrase or the remote user's password. + + +In summary, if you're having trouble talking to the server's ssh +daemon, first make sure that one is running at all. On many systems +it will be installed, but disabled, by default. Once you're done with +this step, you should then check that the server's firewall is +configured to allow incoming connections on the port the ssh daemon is +listening on (usually 22). Don't worry about more exotic +possibilities for misconfiguration until you've checked these two +first. + + +If you're using an authentication agent on the client side to store +passphrases for your keys, you ought to be able to log into the server +without being prompted for a passphrase or a password. If you're +prompted for a passphrase, there are a few possible culprits. + + +You might have forgotten to use ssh-add or + pageant to store the passphrase. + + +You might have stored the passphrase for the wrong key. + + +If you're being prompted for the remote user's password, there are +another few possible problems to check. + + +Either the user's home directory or their .ssh + directory might have excessively liberal permissions. As a result, + the ssh daemon will not trust or read their + authorized_keys file. For example, a group-writable + home or .ssh directory will often cause this symptom. + + +The user's authorized_keys file may have a problem. + If anyone other than the user owns or can write to that file, the + ssh daemon will not trust or read it. + + + +In the ideal world, you should be able to run the following command +successfully, and it should print exactly one line of output, the +current date and time. + + + ssh myserver date + + + +If, on your server, you have login scripts that print banners or other +junk even when running non-interactive commands like this, you should +fix them before you continue, so that they only print output if +they're run interactively. Otherwise these banners will at least +clutter up Mercurial's output. Worse, they could potentially cause +problems with running Mercurial commands remotely. Mercurial makes +tries to detect and ignore banners in non-interactive ssh +sessions, but it is not foolproof. (If you're editing your login +scripts on your server, the usual way to see if a login script is +running in an interactive shell is to check the return code from the +command tty -s.) + + +Once you've verified that plain old ssh is working with your server, +the next step is to ensure that Mercurial runs on the server. The +following command should run successfully: + + + ssh myserver hg version + + +If you see an error message instead of normal hg version output, +this is usually because you haven't installed Mercurial to +/usr/bin. Don't worry if this is the case; you don't need +to do that. But you should check for a few possible problems. + + +Is Mercurial really installed on the server at all? I know this + sounds trivial, but it's worth checking! + + +Maybe your shell's search path (usually set via the PATH + environment variable) is simply misconfigured. + + +Perhaps your PATH environment variable is only being set + to point to the location of the hg executable if the login + session is interactive. This can happen if you're setting the path + in the wrong shell login script. See your shell's documentation for + details. + + +The PYTHONPATH environment variable may need to contain + the path to the Mercurial Python modules. It might not be set at + all; it could be incorrect; or it may be set only if the login is + interactive. + + + +If you can run hg version over an ssh connection, well done! +You've got the server and client sorted out. You should now be able +to use Mercurial to access repositories hosted by that username on +that server. If you run into problems with Mercurial and ssh at this +point, try using the option to get a clearer picture +of what's going on. + + + + +Using compression with ssh + +Mercurial does not compress data when it uses the ssh protocol, +because the ssh protocol can transparently compress data. However, +the default behaviour of ssh clients is not to request +compression. + + +Over any network other than a fast LAN (even a wireless network), +using compression is likely to significantly speed up Mercurial's +network operations. For example, over a WAN, someone measured +compression as reducing the amount of time required to clone a +particularly large repository from 51 minutes to 17 minutes. + + +Both ssh and plink accept a +option which turns on compression. You can easily edit your /.hgrc\ to +enable compression for all of Mercurial's uses of the ssh protocol. + + + [ui] + ssh = ssh -C + + + +If you use ssh, you can configure it to always use +compression when talking to your server. To do this, edit your +.ssh/config file (which may not yet exist), as follows. + + + Host hg + Compression yes + HostName hg.example.com + + +This defines an alias, hg. When you use it on the +ssh command line or in a Mercurial ssh-protocol +URL, it will cause ssh to connect to hg.example.com +and use compression. This gives you both a shorter name to type and +compression, each of which is a good thing in its own right. + + + + + +Serving over HTTP using CGI +\label{sec:collab:cgi} + + +Depending on how ambitious you are, configuring Mercurial's CGI +interface can take anything from a few moments to several hours. + + +We'll begin with the simplest of examples, and work our way towards a +more complex configuration. Even for the most basic case, you're +almost certainly going to need to read and modify your web server's +configuration. + + + + Configuring a web server is a complex, fiddly, and highly + system-dependent activity. I can't possibly give you instructions + that will cover anything like all of the cases you will encounter. + Please use your discretion and judgment in following the sections + below. Be prepared to make plenty of mistakes, and to spend a lot + of time reading your server's error logs. + + + + +Web server configuration checklist + +Before you continue, do take a few moments to check a few aspects of +your system's setup. + + + +Do you have a web server installed at all? Mac OS X ships with + Apache, but many other systems may not have a web server installed. + + +If you have a web server installed, is it actually running? On + most systems, even if one is present, it will be disabled by + default. + + +Is your server configured to allow you to run CGI programs in + the directory where you plan to do so? Most servers default to + explicitly disabling the ability to run CGI programs. + + + +If you don't have a web server installed, and don't have substantial +experience configuring Apache, you should consider using the +lighttpd web server instead of Apache. Apache has a +well-deserved reputation for baroque and confusing configuration. +While lighttpd is less capable in some ways than Apache, most +of these capabilities are not relevant to serving Mercurial +repositories. And lighttpd is undeniably much easier +to get started with than Apache. + + + + +Basic CGI configuration + +On Unix-like systems, it's common for users to have a subdirectory +named something like public_html in their home directory, +from which they can serve up web pages. A file named foo +in this directory will be accessible at a URL of the form +http://www.example.com/\ {username/foo}. + + +To get started, find the hgweb.cgi script that should be +present in your Mercurial installation. If you can't quickly find a +local copy on your system, simply download one from the master +Mercurial repository at +http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi. + + +You'll need to copy this script into your public_html +directory, and ensure that it's executable. + + + cp .../hgweb.cgi /public_html + chmod 755 /public_html/hgweb.cgi + + +The 755 argument to chmod is a little more general +than just making the script executable: it ensures that the script is +executable by anyone, and that group and other write +permissions are not set. If you were to leave those write +permissions enabled, Apache's suexec subsystem would likely +refuse to execute the script. In fact, suexec also insists +that the directory in which the script resides must not be +writable by others. + + + chmod 755 /public_html + + + + +What could <emphasis>possibly</emphasis> go wrong? +\label{sec:collab:wtf} + + +Once you've copied the CGI script into place, go into a web browser, +and try to open the URL http://myhostname/ myuser/hgweb.cgi, +but brace yourself for instant failure. There's a high +probability that trying to visit this URL will fail, and there are +many possible reasons for this. In fact, you're likely to stumble +over almost every one of the possible errors below, so please read +carefully. The following are all of the problems I ran into on a +system running Fedora 7, with a fresh installation of Apache, and a +user account that I created specially to perform this exercise. + + +Your web server may have per-user directories disabled. If you're +using Apache, search your config file for a UserDir +directive. If there's none present, per-user directories will be +disabled. If one exists, but its value is disabled, then +per-user directories will be disabled. Otherwise, the string after +UserDir gives the name of the subdirectory that Apache will +look in under your home directory, for example public_html. + + +Your file access permissions may be too restrictive. The web server +must be able to traverse your home directory and directories under +your public_html directory, and read files under the latter +too. Here's a quick recipe to help you to make your permissions more +appropriate. + + + chmod 755 + find /public_html -type d -print0 | xargs -0r chmod 755 + find /public_html -type f -print0 | xargs -0r chmod 644 + + + +The other possibility with permissions is that you might get a +completely empty window when you try to load the script. In this +case, it's likely that your access permissions are \emph{too + permissive}. Apache's suexec subsystem won't execute a +script that's group- or world-writable, for example. + + +Your web server may be configured to disallow execution of CGI +programs in your per-user web directory. Here's Apache's +default per-user configuration from my Fedora system. + + + <Directory /home/*/public_html> + AllowOverride FileInfo AuthConfig Limit + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + <Limit GET POST OPTIONS> + Order allow,deny + Allow from all + </Limit> + <LimitExcept GET POST OPTIONS> + Order deny,allow + Deny from all + </LimitExcept> + </Directory> + + +If you find a similar-looking Directory group in your Apache +configuration, the directive to look at inside it is Options. +Add ExecCGI to the end of this list if it's missing, and +restart the web server. + + +If you find that Apache serves you the text of the CGI script instead +of executing it, you may need to either uncomment (if already present) +or add a directive like this. + + + AddHandler cgi-script .cgi + + + +The next possibility is that you might be served with a colourful +Python backtrace claiming that it can't import a +mercurial-related module. This is actually progress! The +server is now capable of executing your CGI script. This error is +only likely to occur if you're running a private installation of +Mercurial, instead of a system-wide version. Remember that the web +server runs the CGI program without any of the environment variables +that you take for granted in an interactive session. If this error +happens to you, edit your copy of hgweb.cgi and follow the +directions inside it to correctly set your PYTHONPATH +environment variable. + + +Finally, you are certain to by served with another colourful +Python backtrace: this one will complain that it can't find +/path/to/repository. Edit your hgweb.cgi script +and replace the /path/to/repository string with the complete +path to the repository you want to serve up. + + +At this point, when you try to reload the page, you should be +presented with a nice HTML view of your repository's history. Whew! + + + + +Configuring lighttpd + +To be exhaustive in my experiments, I tried configuring the +increasingly popular lighttpd web server to serve the same +repository as I described with Apache above. I had already overcome +all of the problems I outlined with Apache, many of which are not +server-specific. As a result, I was fairly sure that my file and +directory permissions were good, and that my hgweb.cgi +script was properly edited. + + +Once I had Apache running, getting lighttpd to serve the +repository was a snap (in other words, even if you're trying to use +lighttpd, you should read the Apache section). I first had +to edit the mod_access section of its config file to enable +mod_cgi and mod_userdir, both of which were +disabled by default on my system. I then added a few lines to the end +of the config file, to configure these modules. + + + userdir.path = "public_html" + cgi.assign = ( ".cgi" => "" ) + + +With this done, lighttpd ran immediately for me. If I had +configured lighttpd before Apache, I'd almost certainly have +run into many of the same system-level configuration problems as I did +with Apache. However, I found lighttpd to be noticeably +easier to configure than Apache, even though I've used Apache for over +a decade, and this was my first exposure to lighttpd. + + + + + +Sharing multiple repositories with one CGI script + +The hgweb.cgi script only lets you publish a single +repository, which is an annoying restriction. If you want to publish +more than one without wracking yourself with multiple copies of the +same script, each with different names, a better choice is to use the +hgwebdir.cgi script. + + +The procedure to configure hgwebdir.cgi is only a little +more involved than for hgweb.cgi. First, you must obtain +a copy of the script. If you don't have one handy, you can download a +copy from the master Mercurial repository at +http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi. + + +You'll need to copy this script into your public_html +directory, and ensure that it's executable. + + + cp .../hgwebdir.cgi /public_html + chmod 755 /public_html /public_html/hgwebdir.cgi + + +With basic configuration out of the way, try to visit +http://myhostname/ myuser/hgwebdir.cgi in your browser. It +should display an empty list of repositories. If you get a blank +window or error message, try walking through the list of potential +problems in section . + + +The hgwebdir.cgi script relies on an external +configuration file. By default, it searches for a file named +hgweb.config in the same directory as itself. You'll need +to create this file, and make it world-readable. The format of the +file is similar to a Windows ini file, as understood by Python's +ConfigParser web:configparser module. + + +The easiest way to configure hgwebdir.cgi is with a +section named collections. This will automatically publish +every repository under the directories you name. The section +should look like this: + + + [collections] + /my/root = /my/root + + +Mercurial interprets this by looking at the directory name on the +right hand side of the = sign; finding +repositories in that directory hierarchy; and using the text on the +left to strip off matching text from the names it will actually +list in the web interface. The remaining component of a path after +this stripping has occurred is called a virtual path. + + +Given the example above, if we have a repository whose local path is +/my/root/this/repo, the CGI script will strip the leading +/my/root from the name, and publish the repository with a +virtual path of this/repo. If the base URL for our CGI +script is http://myhostname/ myuser/hgwebdir.cgi, the complete +URL for that repository will be +http://myhostname/ myuser/hgwebdir.cgi/this/repo. + + +If we replace /my/root on the left hand side of this example +with /my, then hgwebdir.cgi will only strip off +/my from the repository name, and will give us a virtual +path of root/this/repo instead of this/repo. + + +The hgwebdir.cgi script will recursively search each +directory listed in the collections section of its +configuration file, but it will not recurse into the +repositories it finds. + + +The collections mechanism makes it easy to publish many +repositories in a fire and forget manner. You only need to set up +the CGI script and configuration file one time. Afterwards, you can +publish or unpublish a repository at any time by simply moving it +into, or out of, the directory hierarchy in which you've configured +hgwebdir.cgi to look. + + + +Explicitly specifying which repositories to publish + +In addition to the collections mechanism, the +hgwebdir.cgi script allows you to publish a specific list +of repositories. To do so, create a paths section, with +contents of the following form. + + + [paths] + repo1 = /my/path/to/some/repo + repo2 = /some/path/to/another + + +In this case, the virtual path (the component that will appear in a +URL) is on the left hand side of each definition, while the path to +the repository is on the right. Notice that there does not need to be +any relationship between the virtual path you choose and the location +of a repository in your filesystem. + + +If you wish, you can use both the collections and +paths mechanisms simultaneously in a single configuration +file. + + + + If multiple repositories have the same virtual path, + hgwebdir.cgi will not report an error. Instead, it will + behave unpredictably. + + + + + + +Downloading source archives + +Mercurial's web interface lets users download an archive of any +revision. This archive will contain a snapshot of the working +directory as of that revision, but it will not contain a copy of the +repository data. + + +By default, this feature is not enabled. To enable it, you'll need to +add an allow_archive item to the web +section of your /.hgrc. + + + + +Web configuration options + +Mercurial's web interfaces (the hg serve command, and the +hgweb.cgi and hgwebdir.cgi scripts) have a +number of configuration options that you can set. These belong in a +section named web. + + +allow_archive: Determines which (if any) archive + download mechanisms Mercurial supports. If you enable this + feature, users of the web interface will be able to download an + archive of whatever revision of a repository they are viewing. + To enable the archive feature, this item must take the form of a + sequence of words drawn from the list below. + + + \item bz2: A tar archive, compressed using + bzip2 compression. This has the best compression ratio, + but uses the most CPU time on the server. + \item gz: A tar archive, compressed using + gzip compression. + \item zip: A zip archive, compressed using LZW + compression. This format has the worst compression ratio, but is + widely used in the Windows world. + + + If you provide an empty list, or don't have an + allow_archive entry at all, this feature will be + disabled. Here is an example of how to enable all three supported + formats. + + + [web] + allow_archive = bz2 gz zip + + +allowpull: Boolean. Determines whether the web + interface allows remote users to hg pull and hg clone this + repository over HTTP. If set to no or false, only + the human-oriented portion of the web interface is available. + + +contact: String. A free-form (but preferably + brief) string identifying the person or group in charge of the + repository. This often contains the name and email address of a + person or mailing list. It often makes sense to place this entry in + a repository's own .hg/hgrc file, but it can make sense + to use in a global /.hgrc\ if every repository has a single + maintainer. + + +maxchanges: Integer. The default maximum number + of changesets to display in a single page of output. + + +maxfiles: Integer. The default maximum number + of modified files to display in a single page of output. + + +stripes: Integer. If the web interface displays + alternating stripes to make it easier to visually align rows + when you are looking at a table, this number controls the number of + rows in each stripe. + + +style: Controls the template Mercurial uses to + display the web interface. Mercurial ships with two web templates, + named default and gitweb (the latter is much more + visually attractive). You can also specify a custom template of + your own; see chapter for details. Here, you + can see how to enable the gitweb style. + + + [web] + style = gitweb + + + + +templates: Path. The directory in which to search + for template files. By default, Mercurial searches in the directory + in which it was installed. + + +If you are using hgwebdir.cgi, you can place a few +configuration items in a web section of the +hgweb.config file instead of a /.hgrc\ file, for +convenience. These items are motd and +style. + + + +Options specific to an individual repository + +A few web configuration items ought to be placed in a +repository's local .hg/hgrc, rather than a user's or +global /.hgrc. + + +description: String. A free-form (but preferably + brief) string that describes the contents or purpose of the + repository. + + +name: String. The name to use for the repository + in the web interface. This overrides the default name, which is the + last component of the repository's path. + + + + + +Options specific to the <command role="hg-cmd">hg serve</command> command + +Some of the items in the web section of a /.hgrc\ file are +only for use with the hg serve command. + + +accesslog: Path. The name of a file into which to + write an access log. By default, the hg serve command writes + this information to standard output, not to a file. Log entries are + written in the standard combined file format used by almost all + web servers. + + +address: String. The local address on which the + server should listen for incoming connections. By default, the + server listens on all addresses. + + +errorlog: Path. The name of a file into which to + write an error log. By default, the hg serve command writes this + information to standard error, not to a file. + + +ipv6: Boolean. Whether to use the IPv6 protocol. + By default, IPv6 is not used. + + +port: Integer. The TCP port number on which the + server should listen. The default port number used is 8000. + + + +\subsubsection{Choosing the right /.hgrc\ file to add web + items to} + + +It is important to remember that a web server like Apache or +lighttpd will run under a user ID that is different to yours. +CGI scripts run by your server, such as hgweb.cgi, will +usually also run under that user ID. + + +If you add web items to your own personal /.hgrc\ file, CGI +scripts won't read that /.hgrc\ file. Those settings will thus only +affect the behaviour of the hg serve command when you run it. To +cause CGI scripts to see your settings, either create a /.hgrc\ file in +the home directory of the user ID that runs your web server, or add +those settings to a system-wide /.hgrc\ file. + + + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch07-filenames.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch07-filenames.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,388 @@ + + + +File names and pattern matching +\label{chap:names} + +Mercurial provides mechanisms that let you work with file names in a +consistent and expressive way. + + +Simple file naming + +Mercurial uses a unified piece of machinery under the hood to +handle file names. Every command behaves uniformly with respect to +file names. The way in which commands work with file names is as +follows. + +If you explicitly name real files on the command line, Mercurial works +with exactly those files, as you would expect. + + +When you provide a directory name, Mercurial will interpret this as +operate on every file in this directory and its subdirectories. +Mercurial traverses the files and subdirectories in a directory in +alphabetical order. When it encounters a subdirectory, it will +traverse that subdirectory before continuing with the current +directory. + + + + +Running commands without any file names + +Mercurial's commands that work with file names have useful default +behaviours when you invoke them without providing any file names or +patterns. What kind of behaviour you should expect depends on what +the command does. Here are a few rules of thumb you can use to +predict what a command is likely to do if you don't give it any names +to work with. + +Most commands will operate on the entire working directory. + This is what the hg add command does, for example. + +If the command has effects that are difficult or impossible to + reverse, it will force you to explicitly provide at least one name + or pattern (see below). This protects you from accidentally + deleting files by running hg remove with no arguments, for + example. + + +It's easy to work around these default behaviours if they don't suit +you. If a command normally operates on the whole working directory, +you can invoke it on just the current directory and its subdirectories +by giving it the name .. + + + +Along the same lines, some commands normally print file names relative +to the root of the repository, even if you're invoking them from a +subdirectory. Such a command will print file names relative to your +subdirectory if you give it explicit names. Here, we're going to run +hg status from a subdirectory, and get it to operate on the +entire working directory while printing file names relative to our +subdirectory, by passing it the output of the hg root command. + + + + + +Telling you what's going on + +The hg add example in the preceding section illustrates something +else that's helpful about Mercurial commands. If a command operates +on a file that you didn't name explicitly on the command line, it will +usually print the name of the file, so that you will not be surprised +what's going on. + + +The principle here is of least surprise. If you've exactly +named a file on the command line, there's no point in repeating it +back at you. If Mercurial is acting on a file implicitly, +because you provided no names, or a directory, or a pattern (see +below), it's safest to tell you what it's doing. + + +For commands that behave this way, you can silence them using the + option. You can also get them to print the name of every +file, even those you've named explicitly, using the +option. + + + + +Using patterns to identify files + +In addition to working with file and directory names, Mercurial lets +you use patterns to identify files. Mercurial's pattern +handling is expressive. + + +On Unix-like systems (Linux, MacOS, etc.), the job of matching file +names to patterns normally falls to the shell. On these systems, you +must explicitly tell Mercurial that a name is a pattern. On Windows, +the shell does not expand patterns, so Mercurial will automatically +identify names that are patterns, and expand them for you. + + +To provide a pattern in place of a regular name on the command line, +the mechanism is simple: + + + syntax:patternbody + + +That is, a pattern is identified by a short text string that says what +kind of pattern this is, followed by a colon, followed by the actual +pattern. + + +Mercurial supports two kinds of pattern syntax. The most frequently +used is called glob; this is the same kind of pattern +matching used by the Unix shell, and should be familiar to Windows +command prompt users, too. + + +When Mercurial does automatic pattern matching on Windows, it uses +glob syntax. You can thus omit the glob: prefix +on Windows, but it's safe to use it, too. + + +The re syntax is more powerful; it lets you specify patterns +using regular expressions, also known as regexps. + + +By the way, in the examples that follow, notice that I'm careful to +wrap all of my patterns in quote characters, so that they won't get +expanded by the shell before Mercurial sees them. + + + +Shell-style <literal>glob</literal> patterns + +This is an overview of the kinds of patterns you can use when you're +matching on glob patterns. + + +The * character matches any string, within a single +directory. + + + +The ** pattern matches any string, and crosses directory +boundaries. It's not a standard Unix glob token, but it's accepted by +several popular Unix shells, and is very useful. + + + +The ? pattern matches any single character. + + + +The [ character begins a character class. This +matches any single character within the class. The class ends with a +] character. A class may contain multiple ranges +of the form a-f, which is shorthand for +abcdef. + +If the first character after the [ in a character class +is a !, it negates the class, making it match any +single character not in the class. + + +A { begins a group of subpatterns, where the whole group +matches if any subpattern in the group matches. The , +character separates subpatterns, and \texttt{}} ends the group. + + + + +Watch out! + +Don't forget that if you want to match a pattern in any directory, you +should not be using the * match-any token, as this will +only match within one directory. Instead, use the ** +token. This small example illustrates the difference between the two. + + + + + + +Regular expression matching with <literal>re</literal> patterns + +Mercurial accepts the same regular expression syntax as the Python +programming language (it uses Python's regexp engine internally). +This is based on the Perl language's regexp syntax, which is the most +popular dialect in use (it's also used in Java, for example). + + +I won't discuss Mercurial's regexp dialect in any detail here, as +regexps are not often used. Perl-style regexps are in any case +already exhaustively documented on a multitude of web sites, and in +many books. Instead, I will focus here on a few things you should +know if you find yourself needing to use regexps with Mercurial. + + +A regexp is matched against an entire file name, relative to the root +of the repository. In other words, even if you're already in +subbdirectory foo, if you want to match files under this +directory, your pattern must start with foo/. + + +One thing to note, if you're familiar with Perl-style regexps, is that +Mercurial's are rooted. That is, a regexp starts matching +against the beginning of a string; it doesn't look for a match +anywhere within the string. To match anywhere in a string, start +your pattern with .*. + + + + + +Filtering files + +Not only does Mercurial give you a variety of ways to specify files; +it lets you further winnow those files using filters. Commands +that work with file names accept two filtering options. + + +, or , lets you specify a pattern + that file names must match in order to be processed. + + +, or , gives you a way to + avoid processing files, if they match this pattern. + + +You can provide multiple and options on the +command line, and intermix them as you please. Mercurial interprets +the patterns you provide using glob syntax by default (but you can use +regexps if you need to). + + +You can read a filter as process only the files that +match this filter. + +The filter is best read as process only the files that +don't match this pattern. + + + + + +Ignoring unwanted files and directories + +XXX. + + + + +Case sensitivity +\label{sec:names:case} + + +If you're working in a mixed development environment that contains +both Linux (or other Unix) systems and Macs or Windows systems, you +should keep in the back of your mind the knowledge that they treat the +case (N versus n) of file names in incompatible ways. This is +not very likely to affect you, and it's easy to deal with if it does, +but it could surprise you if you don't know about it. + + +Operating systems and filesystems differ in the way they handle the +case of characters in file and directory names. There are +three common ways to handle case in names. + + +Completely case insensitive. Uppercase and lowercase versions + of a letter are treated as identical, both when creating a file and + during subsequent accesses. This is common on older DOS-based + systems. + + +Case preserving, but insensitive. When a file or directory is + created, the case of its name is stored, and can be retrieved and + displayed by the operating system. When an existing file is being + looked up, its case is ignored. This is the standard arrangement on + Windows and MacOS. The names foo and FoO + identify the same file. This treatment of uppercase and lowercase + letters as interchangeable is also referred to as \emph{case + folding}. + + +Case sensitive. The case of a name is significant at all times. + The names foo and {FoO} identify different files. This + is the way Linux and Unix systems normally work. + + + +On Unix-like systems, it is possible to have any or all of the above +ways of handling case in action at once. For example, if you use a +USB thumb drive formatted with a FAT32 filesystem on a Linux system, +Linux will handle names on that filesystem in a case preserving, but +insensitive, way. + + + +Safe, portable repository storage + +Mercurial's repository storage mechanism is case safe. It +translates file names so that they can be safely stored on both case +sensitive and case insensitive filesystems. This means that you can +use normal file copying tools to transfer a Mercurial repository onto, +for example, a USB thumb drive, and safely move that drive and +repository back and forth between a Mac, a PC running Windows, and a +Linux box. + + + + +Detecting case conflicts + +When operating in the working directory, Mercurial honours the naming +policy of the filesystem where the working directory is located. If +the filesystem is case preserving, but insensitive, Mercurial will +treat names that differ only in case as the same. + + +An important aspect of this approach is that it is possible to commit +a changeset on a case sensitive (typically Linux or Unix) filesystem +that will cause trouble for users on case insensitive (usually Windows +and MacOS) users. If a Linux user commits changes to two files, one +named myfile.c and the other named MyFile.C, +they will be stored correctly in the repository. And in the working +directories of other Linux users, they will be correctly represented +as separate files. + + +If a Windows or Mac user pulls this change, they will not initially +have a problem, because Mercurial's repository storage mechanism is +case safe. However, once they try to hg update the working +directory to that changeset, or hg merge with that changeset, +Mercurial will spot the conflict between the two file names that the +filesystem would treat as the same, and forbid the update or merge +from occurring. + + + + +Fixing a case conflict + +If you are using Windows or a Mac in a mixed environment where some of +your collaborators are using Linux or Unix, and Mercurial reports a +case folding conflict when you try to hg update or hg merge, +the procedure to fix the problem is simple. + + +Just find a nearby Linux or Unix box, clone the problem repository +onto it, and use Mercurial's hg rename command to change the +names of any offending files or directories so that they will no +longer cause case folding conflicts. Commit this change, hg pull +or hg push it across to your Windows or MacOS system, and +hg update to the revision with the non-conflicting names. + + +The changeset with case-conflicting names will remain in your +project's history, and you still won't be able to hg update your +working directory to that changeset on a Windows or MacOS system, but +you can continue development unimpeded. + + + + Prior to version 0.9.3, Mercurial did not use a case safe repository + storage mechanism, and did not detect case folding conflicts. If + you are using an older version of Mercurial on Windows or MacOS, I + strongly recommend that you upgrade. + + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch08-branch.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch08-branch.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,473 @@ + + + +Managing releases and branchy development +\label{chap:branch} + +Mercurial provides several mechanisms for you to manage a project that +is making progress on multiple fronts at once. To understand these +mechanisms, let's first take a brief look at a fairly normal software +project structure. + +Many software projects issue periodic major releases that contain +substantial new features. In parallel, they may issue minor +releases. These are usually identical to the major releases off which +they're based, but with a few bugs fixed. + +In this chapter, we'll start by talking about how to keep records of +project milestones such as releases. We'll then continue on to talk +about the flow of work between different phases of a project, and how +Mercurial can help you to isolate and manage this work. + + +Giving a persistent name to a revision + +Once you decide that you'd like to call a particular revision a +release, it's a good idea to record the identity of that revision. +This will let you reproduce that release at a later date, for whatever +purpose you might need at the time (reproducing a bug, porting to a +new platform, etc). + + +Mercurial lets you give a permanent name to any revision using the +hg tag command. Not surprisingly, these names are called +tags. + + +A tag is nothing more than a symbolic name for a revision. Tags +exist purely for your convenience, so that you have a handy permanent +way to refer to a revision; Mercurial doesn't interpret the tag names +you use in any way. Neither does Mercurial place any restrictions on +the name of a tag, beyond a few that are necessary to ensure that a +tag can be parsed unambiguously. A tag name cannot contain any of the +following characters: + +Colon (ASCII 58, :) + +Carriage return (ASCII 13, \r) + + +Newline (ASCII 10, \n) + + + +You can use the hg tags command to display the tags present in +your repository. In the output, each tagged revision is identified +first by its name, then by revision number, and finally by the unique +hash of the revision. + +Notice that tip is listed in the output of hg tags. The +tip tag is a special floating tag, which always +identifies the newest revision in the repository. + + +In the output of the hg tags command, tags are listed in reverse +order, by revision number. This usually means that recent tags are +listed before older tags. It also means that tip is always +going to be the first tag listed in the output of hg tags. + + +When you run hg log, if it displays a revision that has tags +associated with it, it will print those tags. + + + +Any time you need to provide a revision ID to a Mercurial command, the +command will accept a tag name in its place. Internally, Mercurial +will translate your tag name into the corresponding revision ID, then +use that. + + + +There's no limit on the number of tags you can have in a repository, +or on the number of tags that a single revision can have. As a +practical matter, it's not a great idea to have too many (a number +which will vary from project to project), simply because tags are +supposed to help you to find revisions. If you have lots of tags, the +ease of using them to identify revisions diminishes rapidly. + + +For example, if your project has milestones as frequent as every few +days, it's perfectly reasonable to tag each one of those. But if you +have a continuous build system that makes sure every revision can be +built cleanly, you'd be introducing a lot of noise if you were to tag +every clean build. Instead, you could tag failed builds (on the +assumption that they're rare!), or simply not use tags to track +buildability. + + +If you want to remove a tag that you no longer want, use +hg tag --remove. + +You can also modify a tag at any time, so that it identifies a +different revision, by simply issuing a new hg tag command. +You'll have to use the option to tell Mercurial that +you really want to update the tag. + +There will still be a permanent record of the previous identity of the +tag, but Mercurial will no longer use it. There's thus no penalty to +tagging the wrong revision; all you have to do is turn around and tag +the correct revision once you discover your error. + + +Mercurial stores tags in a normal revision-controlled file in your +repository. If you've created any tags, you'll find them in a file +named .hgtags. When you run the hg tag command, +Mercurial modifies this file, then automatically commits the change to +it. This means that every time you run hg tag, you'll see a +corresponding changeset in the output of hg log. + + + + +Handling tag conflicts during a merge + +You won't often need to care about the .hgtags file, but +it sometimes makes its presence known during a merge. The format of +the file is simple: it consists of a series of lines. Each line +starts with a changeset hash, followed by a space, followed by the +name of a tag. + + +If you're resolving a conflict in the .hgtags file during +a merge, there's one twist to modifying the .hgtags file: +when Mercurial is parsing the tags in a repository, it never +reads the working copy of the .hgtags file. Instead, it +reads the most recently committed revision of the file. + + +An unfortunate consequence of this design is that you can't actually +verify that your merged .hgtags file is correct until +after you've committed a change. So if you find yourself +resolving a conflict on .hgtags during a merge, be sure to +run hg tags after you commit. If it finds an error in the +.hgtags file, it will report the location of the error, +which you can then fix and commit. You should then run hg tags +again, just to be sure that your fix is correct. + + + + +Tags and cloning + +You may have noticed that the hg clone command has a + option that lets you clone an exact copy of the +repository as of a particular changeset. The new clone will not +contain any project history that comes after the revision you +specified. This has an interaction with tags that can surprise the +unwary. + + +Recall that a tag is stored as a revision to the .hgtags +file, so that when you create a tag, the changeset in which it's +recorded necessarily refers to an older changeset. When you run +hg clone -r foo to clone a repository as of tag +foo, the new clone \emph{will not contain the history that + created the tag} that you used to clone the repository. The result +is that you'll get exactly the right subset of the project's history +in the new repository, but not the tag you might have expected. + + + + +When permanent tags are too much + +Since Mercurial's tags are revision controlled and carried around with +a project's history, everyone you work with will see the tags you +create. But giving names to revisions has uses beyond simply noting +that revision 4237e45506ee is really v2.0.2. If +you're trying to track down a subtle bug, you might want a tag to +remind you of something like Anne saw the symptoms with this +revision. + + +For cases like this, what you might want to use are local tags. +You can create a local tag with the option to the +hg tag command. This will store the tag in a file called +.hg/localtags. Unlike .hgtags, +.hg/localtags is not revision controlled. Any tags you +create using remain strictly local to the repository +you're currently working in. + + + + + +The flow of changes&emdash;big picture vs. little + +To return to the outline I sketched at the beginning of a chapter, +let's think about a project that has multiple concurrent pieces of +work under development at once. + + +There might be a push for a new main release; a new minor bugfix +release to the last main release; and an unexpected hot fix to an +old release that is now in maintenance mode. + + +The usual way people refer to these different concurrent directions of +development is as branches. However, we've already seen numerous +times that Mercurial treats all of history as a series of +branches and merges. Really, what we have here is two ideas that are +peripherally related, but which happen to share a name. + + +Big picture branches represent the sweep of a project's + evolution; people give them names, and talk about them in + conversation. + + +Little picture branches are artefacts of the day-to-day + activity of developing and merging changes. They expose the + narrative of how the code was developed. + + + + + +Managing big-picture branches in repositories + +The easiest way to isolate a big picture branch in Mercurial is in +a dedicated repository. If you have an existing shared +repository&emdash;let's call it myproject&emdash;that reaches a 1.0 +milestone, you can start to prepare for future maintenance releases on +top of version 1.0 by tagging the revision from which you prepared +the 1.0 release. + +You can then clone a new shared myproject-1.0.1 repository as +of that tag. + + + +Afterwards, if someone needs to work on a bug fix that ought to go +into an upcoming 1.0.1 minor release, they clone the +myproject-1.0.1 repository, make their changes, and push them +back. + +Meanwhile, development for the next major release can continue, +isolated and unabated, in the myproject repository. + + + + + +Don't repeat yourself: merging across branches + +In many cases, if you have a bug to fix on a maintenance branch, the +chances are good that the bug exists on your project's main branch +(and possibly other maintenance branches, too). It's a rare developer +who wants to fix the same bug multiple times, so let's look at a few +ways that Mercurial can help you to manage these bugfixes without +duplicating your work. + + +In the simplest instance, all you need to do is pull changes from your +maintenance branch into your local clone of the target branch. + +You'll then need to merge the heads of the two branches, and push back +to the main branch. + + + + + +Naming branches within one repository + +In most instances, isolating branches in repositories is the right +approach. Its simplicity makes it easy to understand; and so it's +hard to make mistakes. There's a one-to-one relationship between +branches you're working in and directories on your system. This lets +you use normal (non-Mercurial-aware) tools to work on files within a +branch/repository. + + +If you're more in the power user category (and your +collaborators are too), there is an alternative way of handling +branches that you can consider. I've already mentioned the +human-level distinction between small picture and big picture +branches. While Mercurial works with multiple small picture +branches in a repository all the time (for example after you pull +changes in, but before you merge them), it can also work with +multiple big picture branches. + + +The key to working this way is that Mercurial lets you assign a +persistent name to a branch. There always exists a branch +named default. Even before you start naming branches +yourself, you can find traces of the default branch if you +look for them. + + +As an example, when you run the hg commit command, and it pops up +your editor so that you can enter a commit message, look for a line +that contains the text HG: branch default at the bottom. +This is telling you that your commit will occur on the branch named +default. + + +To start working with named branches, use the hg branches +command. This command lists the named branches already present in +your repository, telling you which changeset is the tip of each. + +Since you haven't created any named branches yet, the only one that +exists is default. + + +To find out what the current branch is, run the hg branch +command, giving it no arguments. This tells you what branch the +parent of the current changeset is on. + + + +To create a new branch, run the hg branch command again. This +time, give it one argument: the name of the branch you want to create. + + + +After you've created a branch, you might wonder what effect the +hg branch command has had. What do the hg status and +hg tip commands report? + +Nothing has changed in the working directory, and there's been no new +history created. As this suggests, running the hg branch command +has no permanent effect; it only tells Mercurial what branch name to +use the next time you commit a changeset. + + +When you commit a change, Mercurial records the name of the branch on +which you committed. Once you've switched from the default +branch to another and committed, you'll see the name of the new branch +show up in the output of hg log, hg tip, and other commands +that display the same kind of output. + +The hg log-like commands will print the branch name of every +changeset that's not on the default branch. As a result, if +you never use named branches, you'll never see this information. + + +Once you've named a branch and committed a change with that name, +every subsequent commit that descends from that change will inherit +the same branch name. You can change the name of a branch at any +time, using the hg branch command. + +In practice, this is something you won't do very often, as branch +names tend to have fairly long lifetimes. (This isn't a rule, just an +observation.) + + + + +Dealing with multiple named branches in a repository + +If you have more than one named branch in a repository, Mercurial will +remember the branch that your working directory on when you start a +command like hg update or hg pull -u. It will update +the working directory to the tip of this branch, no matter what the +repo-wide tip is. To update to a revision that's on a different +named branch, you may need to use the option to +hg update. + + +This behaviour is a little subtle, so let's see it in action. First, +let's remind ourselves what branch we're currently on, and what +branches are in our repository. + +We're on the bar branch, but there also exists an older +hg foo branch. + + +We can hg update back and forth between the tips of the +foo and bar branches without needing to use the + option, because this only involves going backwards +and forwards linearly through our change history. + + + +If we go back to the foo branch and then run hg update, +it will keep us on foo, not move us to the tip of +bar. + + + +Committing a new change on the foo branch introduces a new +head. + + + + + +Branch names and merging + +As you've probably noticed, merges in Mercurial are not symmetrical. +Let's say our repository has two heads, 17 and 23. If I +hg update to 17 and then hg merge with 23, Mercurial records +17 as the first parent of the merge, and 23 as the second. Whereas if +I hg update to 23 and then hg merge with 17, it records 23 +as the first parent, and 17 as the second. + + +This affects Mercurial's choice of branch name when you merge. After +a merge, Mercurial will retain the branch name of the first parent +when you commit the result of the merge. If your first parent's +branch name is foo, and you merge with bar, the +branch name will still be foo after you merge. + + +It's not unusual for a repository to contain multiple heads, each with +the same branch name. Let's say I'm working on the foo +branch, and so are you. We commit different changes; I pull your +changes; I now have two heads, each claiming to be on the foo +branch. The result of a merge will be a single head on the +foo branch, as you might hope. + + +But if I'm working on the bar branch, and I merge work from +the foo branch, the result will remain on the bar +branch. + + + +To give a more concrete example, if I'm working on the +bleeding-edge branch, and I want to bring in the latest fixes +from the stable branch, Mercurial will choose the right +(bleeding-edge) branch name when I pull and merge from +stable. + + + + +Branch naming is generally useful + +You shouldn't think of named branches as applicable only to situations +where you have multiple long-lived branches cohabiting in a single +repository. They're very useful even in the one-branch-per-repository +case. + + +In the simplest case, giving a name to each branch gives you a +permanent record of which branch a changeset originated on. This +gives you more context when you're trying to follow the history of a +long-lived branchy project. + + +If you're working with shared repositories, you can set up a +pretxnchangegroup hook on each that will block incoming changes +that have the wrong branch name. This provides a simple, but +effective, defence against people accidentally pushing changes from a +bleeding edge branch to a stable branch. Such a hook might +look like this inside the shared repo's /.hgrc. + + + [hooks] + pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch09-undo.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch09-undo.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,963 @@ + + + +Finding and fixing your mistakes +\label{chap:undo} + +To err might be human, but to really handle the consequences well +takes a top-notch revision control system. In this chapter, we'll +discuss some of the techniques you can use when you find that a +problem has crept into your project. Mercurial has some highly +capable features that will help you to isolate the sources of +problems, and to handle them appropriately. + + +Erasing local history + + +The accidental commit + +I have the occasional but persistent problem of typing rather more +quickly than I can think, which sometimes results in me committing a +changeset that is either incomplete or plain wrong. In my case, the +usual kind of incomplete changeset is one in which I've created a new +source file, but forgotten to hg add it. A plain wrong +changeset is not as common, but no less annoying. + + + +Rolling back a transaction +\label{sec:undo:rollback} + +In section , I mentioned that Mercurial treats +each modification of a repository as a transaction. Every time +you commit a changeset or pull changes from another repository, +Mercurial remembers what you did. You can undo, or roll back, +exactly one of these actions using the hg rollback command. (See +section for an important caveat +about the use of this command.) + +Here's a mistake that I often find myself making: committing a change +in which I've created a new file, but forgotten to hg add it. + +Looking at the output of hg status after the commit immediately +confirms the error. + +The commit captured the changes to the file a, but not the +new file b. If I were to push this changeset to a +repository that I shared with a colleague, the chances are high that +something in a would refer to b, which would not +be present in their repository when they pulled my changes. I would +thus become the object of some indignation. + +However, luck is with me&emdash;I've caught my error before I pushed the +changeset. I use the hg rollback command, and Mercurial makes +that last changeset vanish. + +Notice that the changeset is no longer present in the repository's +history, and the working directory once again thinks that the file +a is modified. The commit and rollback have left the +working directory exactly as it was prior to the commit; the changeset +has been completely erased. I can now safely hg add the file +b, and rerun my commit. + + + + +The erroneous pull + +It's common practice with Mercurial to maintain separate development +branches of a project in different repositories. Your development +team might have one shared repository for your project's 0.9 +release, and another, containing different changes, for the 1.0 +release. + +Given this, you can imagine that the consequences could be messy if +you had a local 0.9 repository, and accidentally pulled changes +from the shared 1.0 repository into it. At worst, you could be +paying insufficient attention, and push those changes into the shared +0.9 tree, confusing your entire team (but don't worry, we'll +return to this horror scenario later). However, it's more likely that +you'll notice immediately, because Mercurial will display the URL it's +pulling from, or you will see it pull a suspiciously large number of +changes into the repository. + + +The hg rollback command will work nicely to expunge all of the +changesets that you just pulled. Mercurial groups all changes from +one hg pull into a single transaction, so one hg rollback is +all you need to undo this mistake. + + + + +Rolling back is useless once you've pushed +\label{sec:undo:rollback-after-push} + + +The value of the hg rollback command drops to zero once you've +pushed your changes to another repository. Rolling back a change +makes it disappear entirely, but only in the repository in +which you perform the hg rollback. Because a rollback eliminates +history, there's no way for the disappearance of a change to propagate +between repositories. + + +If you've pushed a change to another repository&emdash;particularly if it's +a shared repository&emdash;it has essentially escaped into the wild, +and you'll have to recover from your mistake in a different way. What +will happen if you push a changeset somewhere, then roll it back, then +pull from the repository you pushed to, is that the changeset will +reappear in your repository. + + +(If you absolutely know for sure that the change you want to roll back +is the most recent change in the repository that you pushed to, +and you know that nobody else could have pulled it from that +repository, you can roll back the changeset there, too, but you really +should really not rely on this working reliably. If you do this, +sooner or later a change really will make it into a repository that +you don't directly control (or have forgotten about), and come back to +bite you.) + + + + +You can only roll back once + +Mercurial stores exactly one transaction in its transaction log; that +transaction is the most recent one that occurred in the repository. +This means that you can only roll back one transaction. If you expect +to be able to roll back one transaction, then its predecessor, this is +not the behaviour you will get. + +Once you've rolled back one transaction in a repository, you can't +roll back again in that repository until you perform another commit or +pull. + + + + + +Reverting the mistaken change + +If you make a modification to a file, and decide that you really +didn't want to change the file at all, and you haven't yet committed +your changes, the hg revert command is the one you'll need. It +looks at the changeset that's the parent of the working directory, and +restores the contents of the file to their state as of that changeset. +(That's a long-winded way of saying that, in the normal case, it +undoes your modifications.) + + +Let's illustrate how the hg revert command works with yet another +small example. We'll begin by modifying a file that Mercurial is +already tracking. + +If we don't want that change, we can simply hg revert the file. + +The hg revert command provides us with an extra degree of safety +by saving our modified file with a .orig extension. + + + +Here is a summary of the cases that the hg revert command can +deal with. We will describe each of these in more detail in the +section that follows. + + +If you modify a file, it will restore the file to its unmodified + state. + + +If you hg add a file, it will undo the added state of + the file, but leave the file itself untouched. + + +If you delete a file without telling Mercurial, it will restore + the file to its unmodified contents. + + +If you use the hg remove command to remove a file, it will + undo the removed state of the file, and restore the file to its + unmodified contents. + + + + +File management errors +\label{sec:undo:mgmt} + + +The hg revert command is useful for more than just modified +files. It lets you reverse the results of all of Mercurial's file +management commands&emdash;hg add, hg remove, and so on. + + +If you hg add a file, then decide that in fact you don't want +Mercurial to track it, use hg revert to undo the add. Don't +worry; Mercurial will not modify the file in any way. It will just +unmark the file. + + + +Similarly, if you ask Mercurial to hg remove a file, you can use +hg revert to restore it to the contents it had as of the parent +of the working directory. + +This works just as well for a file that you deleted by hand, without +telling Mercurial (recall that in Mercurial terminology, this kind of +file is called missing). + + + +If you revert a hg copy, the copied-to file remains in your +working directory afterwards, untracked. Since a copy doesn't affect +the copied-from file in any way, Mercurial doesn't do anything with +the copied-from file. + + + + +A slightly special case: reverting a rename + +If you hg rename a file, there is one small detail that +you should remember. When you hg revert a rename, it's not +enough to provide the name of the renamed-to file, as you can see +here. + +As you can see from the output of hg status, the renamed-to file +is no longer identified as added, but the renamed-from file is +still removed! This is counter-intuitive (at least to me), but at +least it's easy to deal with. + +So remember, to revert a hg rename, you must provide both +the source and destination names. + + +% TODO: the output doesn't look like it will be removed! + + +(By the way, if you rename a file, then modify the renamed-to file, +then revert both components of the rename, when Mercurial restores the +file that was removed as part of the rename, it will be unmodified. +If you need the modifications in the renamed-to file to show up in the +renamed-from file, don't forget to copy them over.) + + +These fiddly aspects of reverting a rename arguably constitute a small +bug in Mercurial. + + + + + + +Dealing with committed changes + +Consider a case where you have committed a change $a$, and another +change $b$ on top of it; you then realise that change $a$ was +incorrect. Mercurial lets you back out an entire changeset +automatically, and building blocks that let you reverse part of a +changeset by hand. + + +Before you read this section, here's something to keep in mind: the +hg backout command undoes changes by adding history, not +by modifying or erasing it. It's the right tool to use if you're +fixing bugs, but not if you're trying to undo some change that has +catastrophic consequences. To deal with those, see +section . + + + +Backing out a changeset + +The hg backout command lets you undo the effects of an entire +changeset in an automated fashion. Because Mercurial's history is +immutable, this command does not get rid of the changeset you +want to undo. Instead, it creates a new changeset that +reverses the effect of the to-be-undone changeset. + + +The operation of the hg backout command is a little intricate, so +let's illustrate it with some examples. First, we'll create a +repository with some simple changes. + + + +The hg backout command takes a single changeset ID as its +argument; this is the changeset to back out. Normally, +hg backout will drop you into a text editor to write a commit +message, so you can record why you're backing the change out. In this +example, we provide a commit message on the command line using the + option. + + + + +Backing out the tip changeset + +We're going to start by backing out the last changeset we committed. + +You can see that the second line from myfile is no longer +present. Taking a look at the output of hg log gives us an idea +of what the hg backout command has done. + +Notice that the new changeset that hg backout has created is a +child of the changeset we backed out. It's easier to see this in +figure , which presents a graphical view of the +change history. As you can see, the history is nice and linear. + + + + + XXX add text + Backing out a change using the hg backout command + \label{fig:undo:backout} + + + + + +Backing out a non-tip change + +If you want to back out a change other than the last one you +committed, pass the option to the +hg backout command. + +This makes backing out any changeset a one-shot operation that's +usually simple and fast. + + + +If you take a look at the contents of myfile after the +backout finishes, you'll see that the first and third changes are +present, but not the second. + + + +As the graphical history in figure +illustrates, Mercurial actually commits two changes in this +kind of situation (the box-shaped nodes are the ones that Mercurial +commits automatically). Before Mercurial begins the backout process, +it first remembers what the current parent of the working directory +is. It then backs out the target changeset, and commits that as a +changeset. Finally, it merges back to the previous parent of the +working directory, and commits the result of the merge. + + +% TODO: to me it looks like mercurial doesn't commit the second merge automatically! + + + + + XXX add text + Automated backout of a non-tip change using the hg backout command + \label{fig:undo:backout-non-tip} + + + +The result is that you end up back where you were, only with some +extra history that undoes the effect of the changeset you wanted to +back out. + + + +Always use the <option role="hg-opt-backout">--merge</option> option + +In fact, since the option will do the right +thing whether or not the changeset you're backing out is the tip +(i.e. it won't try to merge if it's backing out the tip, since there's +no need), you should always use this option when you run the +hg backout command. + + + + + +Gaining more control of the backout process + +While I've recommended that you always use the + option when backing out a change, the +hg backout command lets you decide how to merge a backout +changeset. Taking control of the backout process by hand is something +you will rarely need to do, but it can be useful to understand what +the hg backout command is doing for you automatically. To +illustrate this, let's clone our first repository, but omit the +backout change that it contains. + + + +As with our earlier example, We'll commit a third changeset, then back +out its parent, and see what happens. + +Our new changeset is again a descendant of the changeset we backout +out; it's thus a new head, not a descendant of the changeset +that was the tip. The hg backout command was quite explicit in +telling us this. + + + +Again, it's easier to see what has happened by looking at a graph of +the revision history, in figure . This +makes it clear that when we use hg backout to back out a change +other than the tip, Mercurial adds a new head to the repository (the +change it committed is box-shaped). + + + + + XXX add text + Backing out a change using the hg backout command + \label{fig:undo:backout-manual} + + + +After the hg backout command has completed, it leaves the new +backout changeset as the parent of the working directory. + +Now we have two isolated sets of changes. + + + +Let's think about what we expect to see as the contents of +myfile now. The first change should be present, because +we've never backed it out. The second change should be missing, as +that's the change we backed out. Since the history graph shows the +third change as a separate head, we don't expect to see the +third change present in myfile. + +To get the third change back into the file, we just do a normal merge +of our two heads. + +Afterwards, the graphical history of our repository looks like +figure . + + + + + XXX add text + Manually merging a backout change + \label{fig:undo:backout-manual-merge} + + + + + +Why <command role="hg-cmd">hg backout</command> works as it does + +Here's a brief description of how the hg backout command works. + + +It ensures that the working directory is clean, i.e. that + the output of hg status would be empty. + + +It remembers the current parent of the working directory. Let's + call this changeset orig + + +It does the equivalent of a hg update to sync the working + directory to the changeset you want to back out. Let's call this + changeset backout + + +It finds the parent of that changeset. Let's call that + changeset parent. + + +For each file that the backout changeset affected, it + does the equivalent of a hg revert -r parent on that file, + to restore it to the contents it had before that changeset was + committed. + + +It commits the result as a new changeset. This changeset has + backout as its parent. + + +If you specify on the command line, it + merges with orig, and commits the result of the merge. + + + +An alternative way to implement the hg backout command would be +to hg export the to-be-backed-out changeset as a diff, then use +the option to the patch command to +reverse the effect of the change without fiddling with the working +directory. This sounds much simpler, but it would not work nearly as +well. + + +The reason that hg backout does an update, a commit, a merge, and +another commit is to give the merge machinery the best chance to do a +good job when dealing with all the changes between the change +you're backing out and the current tip. + + +If you're backing out a changeset that's 100 revisions back in your +project's history, the chances that the patch command will +be able to apply a reverse diff cleanly are not good, because +intervening changes are likely to have broken the context that +patch uses to determine whether it can apply a patch (if +this sounds like gibberish, see for a +discussion of the patch command). Also, Mercurial's merge +machinery will handle files and directories being renamed, permission +changes, and modifications to binary files, none of which +patch can deal with. + + + + + +Changes that should never have been +\label{sec:undo:aaaiiieee} + + +Most of the time, the hg backout command is exactly what you need +if you want to undo the effects of a change. It leaves a permanent +record of exactly what you did, both when committing the original +changeset and when you cleaned up after it. + + +On rare occasions, though, you may find that you've committed a change +that really should not be present in the repository at all. For +example, it would be very unusual, and usually considered a mistake, +to commit a software project's object files as well as its source +files. Object files have almost no intrinsic value, and they're +big, so they increase the size of the repository and the amount +of time it takes to clone or pull changes. + + +Before I discuss the options that you have if you commit a brown +paper bag change (the kind that's so bad that you want to pull a +brown paper bag over your head), let me first discuss some approaches +that probably won't work. + + +Since Mercurial treats history as accumulative&emdash;every change builds +on top of all changes that preceded it&emdash;you generally can't just make +disastrous changes disappear. The one exception is when you've just +committed a change, and it hasn't been pushed or pulled into another +repository. That's when you can safely use the hg rollback +command, as I detailed in section . + + +After you've pushed a bad change to another repository, you +could still use hg rollback to make your local copy of the +change disappear, but it won't have the consequences you want. The +change will still be present in the remote repository, so it will +reappear in your local repository the next time you pull. + + +If a situation like this arises, and you know which repositories your +bad change has propagated into, you can try to get rid of the +changeefrom every one of those repositories. This is, of +course, not a satisfactory solution: if you miss even a single +repository while you're expunging, the change is still in the +wild, and could propagate further. + + +If you've committed one or more changes after the change that +you'd like to see disappear, your options are further reduced. +Mercurial doesn't provide a way to punch a hole in history, +leaving changesets intact. + + +XXX This needs filling out. The hg-replay script in the +examples directory works, but doesn't handle merge +changesets. Kind of an important omission. + + + +Protect yourself from <quote>escaped</quote> changes + +If you've committed some changes to your local repository and they've +been pushed or pulled somewhere else, this isn't necessarily a +disaster. You can protect yourself ahead of time against some classes +of bad changeset. This is particularly easy if your team usually +pulls changes from a central repository. + + +By configuring some hooks on that repository to validate incoming +changesets (see chapter ), you can automatically +prevent some kinds of bad changeset from being pushed to the central +repository at all. With such a configuration in place, some kinds of +bad changeset will naturally tend to die out because they can't +propagate into the central repository. Better yet, this happens +without any need for explicit intervention. + + +For instance, an incoming change hook that verifies that a changeset +will actually compile can prevent people from inadvertantly breaking +the build. + + + + + +Finding the source of a bug +\label{sec:undo:bisect} + + +While it's all very well to be able to back out a changeset that +introduced a bug, this requires that you know which changeset to back +out. Mercurial provides an invaluable command, called +hg bisect, that helps you to automate this process and accomplish +it very efficiently. + + +The idea behind the hg bisect command is that a changeset has +introduced some change of behaviour that you can identify with a +simple binary test. You don't know which piece of code introduced the +change, but you know how to test for the presence of the bug. The +hg bisect command uses your test to direct its search for the +changeset that introduced the code that caused the bug. + + +Here are a few scenarios to help you understand how you might apply +this command. + + +The most recent version of your software has a bug that you + remember wasn't present a few weeks ago, but you don't know when it + was introduced. Here, your binary test checks for the presence of + that bug. + + +You fixed a bug in a rush, and now it's time to close the entry + in your team's bug database. The bug database requires a changeset + ID when you close an entry, but you don't remember which changeset + you fixed the bug in. Once again, your binary test checks for the + presence of the bug. + + +Your software works correctly, but runs 15% slower than the + last time you measured it. You want to know which changeset + introduced the performance regression. In this case, your binary + test measures the performance of your software, to see whether it's + fast or slow. + + +The sizes of the components of your project that you ship + exploded recently, and you suspect that something changed in the way + you build your project. + + + +From these examples, it should be clear that the hg bisect +command is not useful only for finding the sources of bugs. You can +use it to find any emergent property of a repository (anything +that you can't find from a simple text search of the files in the +tree) for which you can write a binary test. + + +We'll introduce a little bit of terminology here, just to make it +clear which parts of the search process are your responsibility, and +which are Mercurial's. A test is something that you run +when hg bisect chooses a changeset. A probe is what +hg bisect runs to tell whether a revision is good. Finally, +we'll use the word bisect, as both a noun and a verb, to stand in +for the phrase search using the hg bisect command. + + +One simple way to automate the searching process would be simply to +probe every changeset. However, this scales poorly. If it took ten +minutes to test a single changeset, and you had 10,000 changesets in +your repository, the exhaustive approach would take on average 35 +days to find the changeset that introduced a bug. Even if you +knew that the bug was introduced by one of the last 500 changesets, +and limited your search to those, you'd still be looking at over 40 +hours to find the changeset that introduced your bug. + + +What the hg bisect command does is use its knowledge of the +shape of your project's revision history to perform a search in +time proportional to the logarithm of the number of changesets +to check (the kind of search it performs is called a dichotomic +search). With this approach, searching through 10,000 changesets will +take less than three hours, even at ten minutes per test (the search +will require about 14 tests). Limit your search to the last hundred +changesets, and it will take only about an hour (roughly seven tests). + + +The hg bisect command is aware of the branchy nature of a +Mercurial project's revision history, so it has no problems dealing +with branches, merges, or multiple heads in a repository. It can +prune entire branches of history with a single probe, which is how it +operates so efficiently. + + + +Using the <command role="hg-cmd">hg bisect</command> command + +Here's an example of hg bisect in action. + + + + In versions 0.9.5 and earlier of Mercurial, hg bisect was not a + core command: it was distributed with Mercurial as an extension. + This section describes the built-in command, not the old extension. + + + +Now let's create a repository, so that we can try out the +hg bisect command in isolation. + +We'll simulate a project that has a bug in it in a simple-minded way: +create trivial changes in a loop, and nominate one specific change +that will have the bug. This loop creates 35 changesets, each +adding a single file to the repository. We'll represent our bug +with a file that contains the text i have a gub. + + + +The next thing that we'd like to do is figure out how to use the +hg bisect command. We can use Mercurial's normal built-in help +mechanism for this. + + + +The hg bisect command works in steps. Each step proceeds as follows. + + +You run your binary test. + + + \item If the test succeeded, you tell hg bisect by running the + hg bisect good command. + \item If it failed, run the hg bisect --bad command. + + +The command uses your information to decide which changeset to + test next. + + +It updates the working directory to that changeset, and the + process begins again. + + +The process ends when hg bisect identifies a unique changeset +that marks the point where your test transitioned from succeeding +to failing. + + +To start the search, we must run the hg bisect --reset command. + + + +In our case, the binary test we use is simple: we check to see if any +file in the repository contains the string i have a gub. If it +does, this changeset contains the change that caused the bug. By +convention, a changeset that has the property we're searching for is +bad, while one that doesn't is good. + + +Most of the time, the revision to which the working directory is +synced (usually the tip) already exhibits the problem introduced by +the buggy change, so we'll mark it as bad. + + + +Our next task is to nominate a changeset that we know doesn't +have the bug; the hg bisect command will bracket its search +between the first pair of good and bad changesets. In our case, we +know that revision 10 didn't have the bug. (I'll have more words +about choosing the first good changeset later.) + + + +Notice that this command printed some output. + + +It told us how many changesets it must consider before it can + identify the one that introduced the bug, and how many tests that + will require. + + +It updated the working directory to the next changeset to test, + and told us which changeset it's testing. + + + +We now run our test in the working directory. We use the +grep command to see if our bad file is present in the +working directory. If it is, this revision is bad; if not, this +revision is good. + + + +This test looks like a perfect candidate for automation, so let's turn +it into a shell function. + +We can now run an entire test step with a single command, +mytest. + +A few more invocations of our canned test step command, and we're +done. + + + +Even though we had 40 changesets to search through, the hg bisect +command let us find the changeset that introduced our bug with +only five tests. Because the number of tests that the hg bisect +command performs grows logarithmically with the number of changesets to +search, the advantage that it has over the brute force search +approach increases with every changeset you add. + + + + +Cleaning up after your search + +When you're finished using the hg bisect command in a +repository, you can use the hg bisect reset command to drop +the information it was using to drive your search. The command +doesn't use much space, so it doesn't matter if you forget to run this +command. However, hg bisect won't let you start a new search in +that repository until you do a hg bisect reset. + + + + + + +Tips for finding bugs effectively + + +Give consistent input + +The hg bisect command requires that you correctly report the +result of every test you perform. If you tell it that a test failed +when it really succeeded, it might be able to detect the +inconsistency. If it can identify an inconsistency in your reports, +it will tell you that a particular changeset is both good and bad. +However, it can't do this perfectly; it's about as likely to report +the wrong changeset as the source of the bug. + + + + +Automate as much as possible + +When I started using the hg bisect command, I tried a few times +to run my tests by hand, on the command line. This is an approach +that I, at least, am not suited to. After a few tries, I found that I +was making enough mistakes that I was having to restart my searches +several times before finally getting correct results. + + +My initial problems with driving the hg bisect command by hand +occurred even with simple searches on small repositories; if the +problem you're looking for is more subtle, or the number of tests that +hg bisect must perform increases, the likelihood of operator +error ruining the search is much higher. Once I started automating my +tests, I had much better results. + + +The key to automated testing is twofold: + + +always test for the same symptom, and + + +always feed consistent input to the hg bisect command. + + +In my tutorial example above, the grep command tests for the +symptom, and the if statement takes the result of this check +and ensures that we always feed the same input to the hg bisect +command. The mytest function marries these together in a +reproducible way, so that every test is uniform and consistent. + + + + +Check your results + +Because the output of a hg bisect search is only as good as the +input you give it, don't take the changeset it reports as the +absolute truth. A simple way to cross-check its report is to manually +run your test at each of the following changesets: + + +The changeset that it reports as the first bad revision. Your + test should still report this as bad. + + +The parent of that changeset (either parent, if it's a merge). + Your test should report this changeset as good. + + +A child of that changeset. Your test should report this + changeset as bad. + + + + + +Beware interference between bugs + +It's possible that your search for one bug could be disrupted by the +presence of another. For example, let's say your software crashes at +revision 100, and worked correctly at revision 50. Unknown to you, +someone else introduced a different crashing bug at revision 60, and +fixed it at revision 80. This could distort your results in one of +several ways. + + +It is possible that this other bug completely masks yours, which +is to say that it occurs before your bug has a chance to manifest +itself. If you can't avoid that other bug (for example, it prevents +your project from building), and so can't tell whether your bug is +present in a particular changeset, the hg bisect command cannot +help you directly. Instead, you can mark a changeset as untested by +running hg bisect --skip. + + +A different problem could arise if your test for a bug's presence is +not specific enough. If you check for my program crashes, then +both your crashing bug and an unrelated crashing bug that masks it +will look like the same thing, and mislead hg bisect. + + +Another useful situation in which to use hg bisect --skip is +if you can't test a revision because your project was in a broken and +hence untestable state at that revision, perhaps because someone +checked in a change that prevented the project from building. + + + + +Bracket your search lazily + +Choosing the first good and bad changesets that will mark the +end points of your search is often easy, but it bears a little +discussion nevertheless. From the perspective of hg bisect, the +newest changeset is conventionally bad, and the older +changeset is good. + + +If you're having trouble remembering when a suitable good change +was, so that you can tell hg bisect, you could do worse than +testing changesets at random. Just remember to eliminate contenders +that can't possibly exhibit the bug (perhaps because the feature with +the bug isn't present yet) and those where another problem masks the +bug (as I discussed above). + + +Even if you end up early by thousands of changesets or months of +history, you will only add a handful of tests to the total number that +hg bisect must perform, thanks to its logarithmic behaviour. + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch10-hook.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch10-hook.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,1883 @@ + + + +Handling repository events with hooks +\label{chap:hook} + +Mercurial offers a powerful mechanism to let you perform automated +actions in response to events that occur in a repository. In some +cases, you can even control Mercurial's response to those events. + +The name Mercurial uses for one of these actions is a hook. +Hooks are called triggers in some revision control systems, but +the two names refer to the same idea. + + +An overview of hooks in Mercurial + +Here is a brief list of the hooks that Mercurial supports. We will +revisit each of these hooks in more detail later, in +section . + + +changegroup: This is run after a group of + changesets has been brought into the repository from elsewhere. + +commit: This is run after a new changeset has been + created in the local repository. + +incoming: This is run once for each new changeset + that is brought into the repository from elsewhere. Notice the + difference from changegroup, which is run once per + group of changesets brought in. + +outgoing: This is run after a group of changesets + has been transmitted from this repository. + +prechangegroup: This is run before starting to + bring a group of changesets into the repository. + + +precommit: Controlling. This is run before starting + a commit. + + +preoutgoing: Controlling. This is run before + starting to transmit a group of changesets from this repository. + + +pretag: Controlling. This is run before creating a tag. + + +pretxnchangegroup: Controlling. This is run after a + group of changesets has been brought into the local repository from + another, but before the transaction completes that will make the + changes permanent in the repository. + + +pretxncommit: Controlling. This is run after a new + changeset has been created in the local repository, but before the + transaction completes that will make it permanent. + + +preupdate: Controlling. This is run before starting + an update or merge of the working directory. + + +tag: This is run after a tag is created. + + +update: This is run after an update or merge of the + working directory has finished. + + +Each of the hooks whose description begins with the word +Controlling has the ability to determine whether an activity can +proceed. If the hook succeeds, the activity may proceed; if it fails, +the activity is either not permitted or undone, depending on the hook. + + + + +Hooks and security + + +Hooks are run with your privileges + +When you run a Mercurial command in a repository, and the command +causes a hook to run, that hook runs on your system, under +your user account, with your privilege level. Since +hooks are arbitrary pieces of executable code, you should treat them +with an appropriate level of suspicion. Do not install a hook unless +you are confident that you know who created it and what it does. + + +In some cases, you may be exposed to hooks that you did not install +yourself. If you work with Mercurial on an unfamiliar system, +Mercurial will run hooks defined in that system's global /.hgrc\ file. + + +If you are working with a repository owned by another user, Mercurial +can run hooks defined in that user's repository, but it will still run +them as you. For example, if you hg pull from that +repository, and its .hg/hgrc defines a local +outgoing hook, that hook will run under your user account, even +though you don't own that repository. + + + + This only applies if you are pulling from a repository on a local or + network filesystem. If you're pulling over http or ssh, any + outgoing hook will run under whatever account is executing + the server process, on the server. + + + +XXX To see what hooks are defined in a repository, use the +hg config hooks command. If you are working in one +repository, but talking to another that you do not own (e.g. using +hg pull or hg incoming), remember that it is the other +repository's hooks you should be checking, not your own. + + + + +Hooks do not propagate + +In Mercurial, hooks are not revision controlled, and do not propagate +when you clone, or pull from, a repository. The reason for this is +simple: a hook is a completely arbitrary piece of executable code. It +runs under your user identity, with your privilege level, on your +machine. + + +It would be extremely reckless for any distributed revision control +system to implement revision-controlled hooks, as this would offer an +easily exploitable way to subvert the accounts of users of the +revision control system. + + +Since Mercurial does not propagate hooks, if you are collaborating +with other people on a common project, you should not assume that they +are using the same Mercurial hooks as you are, or that theirs are +correctly configured. You should document the hooks you expect people +to use. + + +In a corporate intranet, this is somewhat easier to control, as you +can for example provide a standard installation of Mercurial on an +NFS filesystem, and use a site-wide /.hgrc\ file to define hooks that +all users will see. However, this too has its limits; see below. + + + + +Hooks can be overridden + +Mercurial allows you to override a hook definition by redefining the +hook. You can disable it by setting its value to the empty string, or +change its behaviour as you wish. + + +If you deploy a system- or site-wide /.hgrc\ file that defines some +hooks, you should thus understand that your users can disable or +override those hooks. + + + + +Ensuring that critical hooks are run + +Sometimes you may want to enforce a policy that you do not want others +to be able to work around. For example, you may have a requirement +that every changeset must pass a rigorous set of tests. Defining this +requirement via a hook in a site-wide /.hgrc\ won't work for remote +users on laptops, and of course local users can subvert it at will by +overriding the hook. + + +Instead, you can set up your policies for use of Mercurial so that +people are expected to propagate changes through a well-known +canonical server that you have locked down and configured +appropriately. + + +One way to do this is via a combination of social engineering and +technology. Set up a restricted-access account; users can push +changes over the network to repositories managed by this account, but +they cannot log into the account and run normal shell commands. In +this scenario, a user can commit a changeset that contains any old +garbage they want. + + +When someone pushes a changeset to the server that everyone pulls +from, the server will test the changeset before it accepts it as +permanent, and reject it if it fails to pass the test suite. If +people only pull changes from this filtering server, it will serve to +ensure that all changes that people pull have been automatically +vetted. + + + + + +Care with <literal>pretxn</literal> hooks in a shared-access repository + +If you want to use hooks to do some automated work in a repository +that a number of people have shared access to, you need to be careful +in how you do this. + + +Mercurial only locks a repository when it is writing to the +repository, and only the parts of Mercurial that write to the +repository pay attention to locks. Write locks are necessary to +prevent multiple simultaneous writers from scribbling on each other's +work, corrupting the repository. + + +Because Mercurial is careful with the order in which it reads and +writes data, it does not need to acquire a lock when it wants to read +data from the repository. The parts of Mercurial that read from the +repository never pay attention to locks. This lockless reading scheme +greatly increases performance and concurrency. + + +With great performance comes a trade-off, though, one which has the +potential to cause you trouble unless you're aware of it. To describe +this requires a little detail about how Mercurial adds changesets to a +repository and reads those changes. + + +When Mercurial writes metadata, it writes it straight into the +destination file. It writes file data first, then manifest data +(which contains pointers to the new file data), then changelog data +(which contains pointers to the new manifest data). Before the first +write to each file, it stores a record of where the end of the file +was in its transaction log. If the transaction must be rolled back, +Mercurial simply truncates each file back to the size it was before the +transaction began. + + +When Mercurial reads metadata, it reads the changelog first, +then everything else. Since a reader will only access parts of the +manifest or file metadata that it can see in the changelog, it can +never see partially written data. + + +Some controlling hooks (pretxncommit and +pretxnchangegroup) run when a transaction is almost complete. +All of the metadata has been written, but Mercurial can still roll the +transaction back and cause the newly-written data to disappear. + + +If one of these hooks runs for long, it opens a window of time during +which a reader can see the metadata for changesets that are not yet +permanent, and should not be thought of as really there. The +longer the hook runs, the longer that window is open. + + + +The problem illustrated + +In principle, a good use for the pretxnchangegroup hook would +be to automatically build and test incoming changes before they are +accepted into a central repository. This could let you guarantee that +nobody can push changes to this repository that break the build. +But if a client can pull changes while they're being tested, the +usefulness of the test is zero; an unsuspecting someone can pull +untested changes, potentially breaking their build. + + +The safest technological answer to this challenge is to set up such a +gatekeeper repository as unidirectional. Let it take +changes pushed in from the outside, but do not allow anyone to pull +changes from it (use the preoutgoing hook to lock it down). +Configure a changegroup hook so that if a build or test +succeeds, the hook will push the new changes out to another repository +that people can pull from. + + +In practice, putting a centralised bottleneck like this in place is +not often a good idea, and transaction visibility has nothing to do +with the problem. As the size of a project&emdash;and the time it takes to +build and test&emdash;grows, you rapidly run into a wall with this try +before you buy approach, where you have more changesets to test than +time in which to deal with them. The inevitable result is frustration +on the part of all involved. + + +An approach that scales better is to get people to build and test +before they push, then run automated builds and tests centrally +after a push, to be sure all is well. The advantage of this +approach is that it does not impose a limit on the rate at which the +repository can accept changes. + + + + + +A short tutorial on using hooks +\label{sec:hook:simple} + + +It is easy to write a Mercurial hook. Let's start with a hook that +runs when you finish a hg commit, and simply prints the hash of +the changeset you just created. The hook is called commit. + + + + + A simple hook that runs when a changeset is committed + \label{ex:hook:init} + + + +All hooks follow the pattern in example . You add +an entry to the hooks section of your /.hgrc. On the left +is the name of the event to trigger on; on the right is the action to +take. As you can see, you can run an arbitrary shell command in a +hook. Mercurial passes extra information to the hook using +environment variables (look for HG_NODE in the example). + + + +Performing multiple actions per event + +Quite often, you will want to define more than one hook for a +particular kind of event, as shown in example . +Mercurial lets you do this by adding an extension to the end of +a hook's name. You extend a hook's name by giving the name of the +hook, followed by a full stop (the . character), followed +by some more text of your choosing. For example, Mercurial will run +both commit.foo and commit.bar when the +commit event occurs. + + + + + Defining a second commit hook + \label{ex:hook:ext} + + + +To give a well-defined order of execution when there are multiple +hooks defined for an event, Mercurial sorts hooks by extension, and +executes the hook commands in this sorted order. In the above +example, it will execute commit.bar before +commit.foo, and commit before both. + + +It is a good idea to use a somewhat descriptive extension when you +define a new hook. This will help you to remember what the hook was +for. If the hook fails, you'll get an error message that contains the +hook name and extension, so using a descriptive extension could give +you an immediate hint as to why the hook failed (see +section for an example). + + + + +Controlling whether an activity can proceed +\label{sec:hook:perm} + + +In our earlier examples, we used the commit hook, which is +run after a commit has completed. This is one of several Mercurial +hooks that run after an activity finishes. Such hooks have no way of +influencing the activity itself. + + +Mercurial defines a number of events that occur before an activity +starts; or after it starts, but before it finishes. Hooks that +trigger on these events have the added ability to choose whether the +activity can continue, or will abort. + + +The pretxncommit hook runs after a commit has all but +completed. In other words, the metadata representing the changeset +has been written out to disk, but the transaction has not yet been +allowed to complete. The pretxncommit hook has the ability to +decide whether the transaction can complete, or must be rolled back. + + +If the pretxncommit hook exits with a status code of zero, the +transaction is allowed to complete; the commit finishes; and the +commit hook is run. If the pretxncommit hook exits with +a non-zero status code, the transaction is rolled back; the metadata +representing the changeset is erased; and the commit hook is +not run. + + + + + Using the pretxncommit hook to control commits + \label{ex:hook:pretxncommit} + + + +The hook in example checks that a commit +comment contains a bug ID. If it does, the commit can complete. If +not, the commit is rolled back. + + + + + +Writing your own hooks + +When you are writing a hook, you might find it useful to run Mercurial +either with the option, or the verbose config +item set to true. When you do so, Mercurial will print a message +before it calls each hook. + + + +Choosing how your hook should run +\label{sec:hook:lang} + + +You can write a hook either as a normal program&emdash;typically a shell +script&emdash;or as a Python function that is executed within the Mercurial +process. + + +Writing a hook as an external program has the advantage that it +requires no knowledge of Mercurial's internals. You can call normal +Mercurial commands to get any added information you need. The +trade-off is that external hooks are slower than in-process hooks. + + +An in-process Python hook has complete access to the Mercurial API, +and does not shell out to another process, so it is inherently +faster than an external hook. It is also easier to obtain much of the +information that a hook requires by using the Mercurial API than by +running Mercurial commands. + + +If you are comfortable with Python, or require high performance, +writing your hooks in Python may be a good choice. However, when you +have a straightforward hook to write and you don't need to care about +performance (probably the majority of hooks), a shell script is +perfectly fine. + + + + +Hook parameters +\label{sec:hook:param} + + +Mercurial calls each hook with a set of well-defined parameters. In +Python, a parameter is passed as a keyword argument to your hook +function. For an external program, a parameter is passed as an +environment variable. + + +Whether your hook is written in Python or as a shell script, the +hook-specific parameter names and values will be the same. A boolean +parameter will be represented as a boolean value in Python, but as the +number 1 (for true) or 0 (for false) as an environment +variable for an external hook. If a hook parameter is named +foo, the keyword argument for a Python hook will also be +named foo, while the environment variable for an external +hook will be named HG_FOO. + + + + +Hook return values and activity control + +A hook that executes successfully must exit with a status of zero if +external, or return boolean false if in-process. Failure is +indicated with a non-zero exit status from an external hook, or an +in-process hook returning boolean true. If an in-process hook +raises an exception, the hook is considered to have failed. + + +For a hook that controls whether an activity can proceed, zero/false +means allow, while non-zero/true/exception means deny. + + + + +Writing an external hook + +When you define an external hook in your /.hgrc\ and the hook is run, +its value is passed to your shell, which interprets it. This means +that you can use normal shell constructs in the body of the hook. + + +An executable hook is always run with its current directory set to a +repository's root directory. + + +Each hook parameter is passed in as an environment variable; the name +is upper-cased, and prefixed with the string HG_. + + +With the exception of hook parameters, Mercurial does not set or +modify any environment variables when running a hook. This is useful +to remember if you are writing a site-wide hook that may be run by a +number of different users with differing environment variables set. +In multi-user situations, you should not rely on environment variables +being set to the values you have in your environment when testing the +hook. + + + + +Telling Mercurial to use an in-process hook + +The /.hgrc\ syntax for defining an in-process hook is slightly +different than for an executable hook. The value of the hook must +start with the text python:, and continue with the +fully-qualified name of a callable object to use as the hook's value. + + +The module in which a hook lives is automatically imported when a hook +is run. So long as you have the module name and PYTHONPATH +right, it should just work. + + +The following /.hgrc\ example snippet illustrates the syntax and +meaning of the notions we just described. + + + [hooks] + commit.example = python:mymodule.submodule.myhook + + +When Mercurial runs the commit.example hook, it imports +mymodule.submodule, looks for the callable object named +myhook, and calls it. + + + + +Writing an in-process hook + +The simplest in-process hook does nothing, but illustrates the basic +shape of the hook API: + + + def myhook(ui, repo, **kwargs): + pass + + +The first argument to a Python hook is always a +ui object. The second is a repository object; +at the moment, it is always an instance of +localrepository. Following these two +arguments are other keyword arguments. Which ones are passed in +depends on the hook being called, but a hook can ignore arguments it +doesn't care about by dropping them into a keyword argument dict, as +with **kwargs above. + + + + + +Some hook examples + + +Writing meaningful commit messages + +It's hard to imagine a useful commit message being very short. The +simple pretxncommit hook of figure +will prevent you from committing a changeset with a message that is +less than ten bytes long. + + + + + A hook that forbids overly short commit messages + \label{ex:hook:msglen.go} + + + + + +Checking for trailing whitespace + +An interesting use of a commit-related hook is to help you to write +cleaner code. A simple example of cleaner code is the dictum that +a change should not add any new lines of text that contain trailing +whitespace. Trailing whitespace is a series of space and tab +characters at the end of a line of text. In most cases, trailing +whitespace is unnecessary, invisible noise, but it is occasionally +problematic, and people often prefer to get rid of it. + + +You can use either the precommit or pretxncommit hook to +tell whether you have a trailing whitespace problem. If you use the +precommit hook, the hook will not know which files you are +committing, so it will have to check every modified file in the +repository for trailing white space. If you want to commit a change +to just the file foo, but the file bar contains +trailing whitespace, doing a check in the precommit hook will +prevent you from committing foo due to the problem with +bar. This doesn't seem right. + + +Should you choose the pretxncommit hook, the check won't occur +until just before the transaction for the commit completes. This will +allow you to check for problems only the exact files that are being +committed. However, if you entered the commit message interactively +and the hook fails, the transaction will roll back; you'll have to +re-enter the commit message after you fix the trailing whitespace and +run hg commit again. + + + + + A simple hook that checks for trailing whitespace + \label{ex:hook:ws.simple} + + + +Figure introduces a simple pretxncommit +hook that checks for trailing whitespace. This hook is short, but not +very helpful. It exits with an error status if a change adds a line +with trailing whitespace to any file, but does not print any +information that might help us to identify the offending file or +line. It also has the nice property of not paying attention to +unmodified lines; only lines that introduce new trailing whitespace +cause problems. + + + + + A better trailing whitespace hook + \label{ex:hook:ws.better} + + + +The example of figure is much more complex, +but also more useful. It parses a unified diff to see if any lines +add trailing whitespace, and prints the name of the file and the line +number of each such occurrence. Even better, if the change adds +trailing whitespace, this hook saves the commit comment and prints the +name of the save file before exiting and telling Mercurial to roll the +transaction back, so you can use +hg commit filename to reuse the +saved commit message once you've corrected the problem. + + +As a final aside, note in figure the use of +perl's in-place editing feature to get rid of trailing +whitespace from a file. This is concise and useful enough that I will +reproduce it here. + + + perl -pi -e 's,\textbackslash{}s+$,,' filename + + + + + + +Bundled hooks + +Mercurial ships with several bundled hooks. You can find them in the +hgext directory of a Mercurial source tree. If you are +using a Mercurial binary package, the hooks will be located in the +hgext directory of wherever your package installer put +Mercurial. + + + +<literal role="hg-ext">acl</literal>&emdash;access control for parts of a repository + +The acl extension lets you control which remote users are +allowed to push changesets to a networked server. You can protect any +portion of a repository (including the entire repo), so that a +specific remote user can push changes that do not affect the protected +portion. + + +This extension implements access control based on the identity of the +user performing a push, not on who committed the changesets +they're pushing. It makes sense to use this hook only if you have a +locked-down server environment that authenticates remote users, and +you want to be sure that only specific users are allowed to push +changes to that server. + + + +Configuring the <literal role="hook">acl</literal> hook + +In order to manage incoming changesets, the acl hook must be +used as a pretxnchangegroup hook. This lets it see which files +are modified by each incoming changeset, and roll back a group of +changesets if they modify forbidden files. Example: + + + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook + + + +The acl extension is configured using three sections. + + +The acl section has only one entry, sources, +which lists the sources of incoming changesets that the hook should +pay attention to. You don't normally need to configure this section. + + +serve: Control incoming changesets that are arriving + from a remote repository over http or ssh. This is the default + value of sources, and usually the only setting you'll + need for this configuration item. + + +pull: Control incoming changesets that are + arriving via a pull from a local repository. + + +push: Control incoming changesets that are + arriving via a push from a local repository. + + +bundle: Control incoming changesets that are + arriving from another repository via a bundle. + + + +The acl.allow section controls the users that are allowed to +add changesets to the repository. If this section is not present, all +users that are not explicitly denied are allowed. If this section is +present, all users that are not explicitly allowed are denied (so an +empty section means that all users are denied). + + +The acl.deny section determines which users are denied +from adding changesets to the repository. If this section is not +present or is empty, no users are denied. + + +The syntaxes for the acl.allow and acl.deny +sections are identical. On the left of each entry is a glob pattern +that matches files or directories, relative to the root of the +repository; on the right, a user name. + + +In the following example, the user docwriter can only push +changes to the docs subtree of the repository, while +intern can push changes to any file or directory except +source/sensitive. + + + [acl.allow] + docs/** = docwriter + + + [acl.deny] + source/sensitive/** = intern + + + + + +Testing and troubleshooting + +If you want to test the acl hook, run it with Mercurial's +debugging output enabled. Since you'll probably be running it on a +server where it's not convenient (or sometimes possible) to pass in +the option, don't forget that you can enable +debugging output in your /.hgrc: + + + [ui] + debug = true + + +With this enabled, the acl hook will print enough information +to let you figure out why it is allowing or forbidding pushes from +specific users. + + + + + +<literal role="hg-ext">bugzilla</literal>&emdash;integration with Bugzilla + +The bugzilla extension adds a comment to a Bugzilla bug +whenever it finds a reference to that bug ID in a commit comment. You +can install this hook on a shared server, so that any time a remote +user pushes changes to this server, the hook gets run. + + +It adds a comment to the bug that looks like this (you can configure +the contents of the comment&emdash;see below): + + + Changeset aad8b264143a, made by Joe User <joe.user@domain.com> in + the frobnitz repository, refers to this bug. + + + For complete details, see + http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a + + + Changeset description: + Fix bug 10483 by guarding against some NULL pointers + + +The value of this hook is that it automates the process of updating a +bug any time a changeset refers to it. If you configure the hook +properly, it makes it easy for people to browse straight from a +Bugzilla bug to a changeset that refers to that bug. + + +You can use the code in this hook as a starting point for some more +exotic Bugzilla integration recipes. Here are a few possibilities: + + +Require that every changeset pushed to the server have a valid + bug ID in its commit comment. In this case, you'd want to configure + the hook as a pretxncommit hook. This would allow the hook + to reject changes that didn't contain bug IDs. + + +Allow incoming changesets to automatically modify the + state of a bug, as well as simply adding a comment. For + example, the hook could recognise the string fixed bug 31337 as + indicating that it should update the state of bug 31337 to + requires testing. + + + + +Configuring the <literal role="hook">bugzilla</literal> hook +\label{sec:hook:bugzilla:config} + + +You should configure this hook in your server's /.hgrc\ as an +incoming hook, for example as follows: + + + [hooks] + incoming.bugzilla = python:hgext.bugzilla.hook + + + +Because of the specialised nature of this hook, and because Bugzilla +was not written with this kind of integration in mind, configuring +this hook is a somewhat involved process. + + +Before you begin, you must install the MySQL bindings for Python on +the host(s) where you'll be running the hook. If this is not +available as a binary package for your system, you can download it +from web:mysql-python. + + +Configuration information for this hook lives in the +bugzilla section of your /.hgrc. + + +version: The version of Bugzilla installed on + the server. The database schema that Bugzilla uses changes + occasionally, so this hook has to know exactly which schema to use. + At the moment, the only version supported is 2.16. + + +host: The hostname of the MySQL server that + stores your Bugzilla data. The database must be configured to allow + connections from whatever host you are running the bugzilla + hook on. + + +user: The username with which to connect to + the MySQL server. The database must be configured to allow this + user to connect from whatever host you are running the + bugzilla hook on. This user must be able to access and + modify Bugzilla tables. The default value of this item is + bugs, which is the standard name of the Bugzilla user in a + MySQL database. + + +password: The MySQL password for the user you + configured above. This is stored as plain text, so you should make + sure that unauthorised users cannot read the /.hgrc\ file where you + store this information. + + +db: The name of the Bugzilla database on the + MySQL server. The default value of this item is bugs, + which is the standard name of the MySQL database where Bugzilla + stores its data. + + +notify: If you want Bugzilla to send out a + notification email to subscribers after this hook has added a + comment to a bug, you will need this hook to run a command whenever + it updates the database. The command to run depends on where you + have installed Bugzilla, but it will typically look something like + this, if you have Bugzilla installed in + /var/www/html/bugzilla: + + + cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com + + + The Bugzilla processmail program expects to be given a + bug ID (the hook replaces %s with the bug ID) and an + email address. It also expects to be able to write to some files in + the directory that it runs in. If Bugzilla and this hook are not + installed on the same machine, you will need to find a way to run + processmail on the server where Bugzilla is installed. + + + + + +Mapping committer names to Bugzilla user names + +By default, the bugzilla hook tries to use the email address +of a changeset's committer as the Bugzilla user name with which to +update a bug. If this does not suit your needs, you can map committer +email addresses to Bugzilla user names using a usermap +section. + + +Each item in the usermap section contains an email address +on the left, and a Bugzilla user name on the right. + + + [usermap] + jane.user@example.com = jane + + +You can either keep the usermap data in a normal /.hgrc, or +tell the bugzilla hook to read the information from an +external usermap file. In the latter case, you can store +usermap data by itself in (for example) a user-modifiable +repository. This makes it possible to let your users maintain their +own usermap entries. The main /.hgrc\ file might +look like this: + + + # regular hgrc file refers to external usermap file + [bugzilla] + usermap = /home/hg/repos/userdata/bugzilla-usermap.conf + + +While the usermap file that it refers to might look like +this: + + + # bugzilla-usermap.conf - inside a hg repository + [usermap] + stephanie@example.com = steph + + + + + +Configuring the text that gets added to a bug + +You can configure the text that this hook adds as a comment; you +specify it in the form of a Mercurial template. Several /.hgrc\ +entries (still in the bugzilla section) control this +behaviour. + + +strip: The number of leading path elements to strip + from a repository's path name to construct a partial path for a URL. + For example, if the repositories on your server live under + /home/hg/repos, and you have a repository whose path is + /home/hg/repos/app/tests, then setting strip to + 4 will give a partial path of app/tests. The + hook will make this partial path available when expanding a + template, as webroot. + + +template: The text of the template to use. In addition + to the usual changeset-related variables, this template can use + hgweb (the value of the hgweb configuration item + above) and webroot (the path constructed using + strip above). + + + +In addition, you can add a baseurl item to the +web section of your /.hgrc. The bugzilla hook will +make this available when expanding a template, as the base string to +use when constructing a URL that will let users browse from a Bugzilla +comment to view a changeset. Example: + + + [web] + baseurl = http://hg.domain.com/ + + + +Here is an example set of bugzilla hook config information. + + + [bugzilla] + host = bugzilla.example.com + password = mypassword + version = 2.16 + # server-side repos live in /home/hg/repos, so strip 4 leading + # separators + strip = 4 + hgweb = http://hg.example.com/ + usermap = /home/hg/repos/notify/bugzilla.conf + template = Changeset {node|short}, made by {author} in the {webroot} + repo, refers to this bug.\\nFor complete details, see + {hgweb}{webroot}?cmd=changeset;node={node|short}\\nChangeset + description:\\n\\t{desc|tabindent} + + + + + +Testing and troubleshooting + +The most common problems with configuring the bugzilla hook +relate to running Bugzilla's processmail script and mapping +committer names to user names. + + +Recall from section above that the user +that runs the Mercurial process on the server is also the one that +will run the processmail script. The +processmail script sometimes causes Bugzilla to write to +files in its configuration directory, and Bugzilla's configuration +files are usually owned by the user that your web server runs under. + + +You can cause processmail to be run with the suitable +user's identity using the sudo command. Here is an example +entry for a sudoers file. + + + hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s + + +This allows the hg_user user to run a +processmail-wrapper program under the identity of +httpd_user. + + +This indirection through a wrapper script is necessary, because +processmail expects to be run with its current directory +set to wherever you installed Bugzilla; you can't specify that kind of +constraint in a sudoers file. The contents of the wrapper +script are simple: + + + #!/bin/sh + cd `dirname $0` && ./processmail "$1" nobody@example.com + + +It doesn't seem to matter what email address you pass to +processmail. + + +If your usermap is not set up correctly, users will see an +error message from the bugzilla hook when they push changes +to the server. The error message will look like this: + + + cannot find bugzilla user id for john.q.public@example.com + + +What this means is that the committer's address, +john.q.public@example.com, is not a valid Bugzilla user name, +nor does it have an entry in your usermap that maps it to +a valid Bugzilla user name. + + + + + +<literal role="hg-ext">notify</literal>&emdash;send email notifications + +Although Mercurial's built-in web server provides RSS feeds of changes +in every repository, many people prefer to receive change +notifications via email. The notify hook lets you send out +notifications to a set of email addresses whenever changesets arrive +that those subscribers are interested in. + + +As with the bugzilla hook, the notify hook is +template-driven, so you can customise the contents of the notification +messages that it sends. + + +By default, the notify hook includes a diff of every changeset +that it sends out; you can limit the size of the diff, or turn this +feature off entirely. It is useful for letting subscribers review +changes immediately, rather than clicking to follow a URL. + + + +Configuring the <literal role="hg-ext">notify</literal> hook + +You can set up the notify hook to send one email message per +incoming changeset, or one per incoming group of changesets (all those +that arrived in a single pull or push). + + + [hooks] + # send one email per group of changes + changegroup.notify = python:hgext.notify.hook + # send one email per change + incoming.notify = python:hgext.notify.hook + + + +Configuration information for this hook lives in the +notify section of a /.hgrc\ file. + + +test: By default, this hook does not send out + email at all; instead, it prints the message that it would + send. Set this item to false to allow email to be sent. + The reason that sending of email is turned off by default is that it + takes several tries to configure this extension exactly as you would + like, and it would be bad form to spam subscribers with a number of + broken notifications while you debug your configuration. + + +config: The path to a configuration file that + contains subscription information. This is kept separate from the + main /.hgrc\ so that you can maintain it in a repository of its own. + People can then clone that repository, update their subscriptions, + and push the changes back to your server. + + +strip: The number of leading path separator + characters to strip from a repository's path, when deciding whether + a repository has subscribers. For example, if the repositories on + your server live in /home/hg/repos, and notify is + considering a repository named /home/hg/repos/shared/test, + setting strip to 4 will cause + notify to trim the path it considers down to + shared/test, and it will match subscribers against that. + + +template: The template text to use when sending + messages. This specifies both the contents of the message header + and its body. + + +maxdiff: The maximum number of lines of diff + data to append to the end of a message. If a diff is longer than + this, it is truncated. By default, this is set to 300. Set this to + 0 to omit diffs from notification emails. + + +sources: A list of sources of changesets to + consider. This lets you limit notify to only sending out + email about changes that remote users pushed into this repository + via a server, for example. See section for + the sources you can specify here. + + + +If you set the baseurl item in the web +section, you can use it in a template; it will be available as +webroot. + + +Here is an example set of notify configuration information. + + + [notify] + # really send email + test = false + # subscriber data lives in the notify repo + config = /home/hg/repos/notify/notify.conf + # repos live in /home/hg/repos on server, so strip 4 "/" chars + strip = 4 + template = X-Hg-Repo: {webroot} + Subject: {webroot}: {desc|firstline|strip} + From: {author} + + + changeset {node|short} in {root} + details: {baseurl}{webroot}?cmd=changeset;node={node|short} + description: + {desc|tabindent|strip} + + + [web] + baseurl = http://hg.example.com/ + + + +This will produce a message that looks like the following: + + + X-Hg-Repo: tests/slave + Subject: tests/slave: Handle error case when slave has no buffers + Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) + + + changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave + details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 + description: + Handle error case when slave has no buffers + diffs (54 lines): + + + diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h + &emdash; a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 + +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 + @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h) + [...snip...] + + + + + +Testing and troubleshooting + +Do not forget that by default, the notify extension \emph{will + not send any mail} until you explicitly configure it to do so, by +setting test to false. Until you do that, +it simply prints the message it would send. + + + + + + +Information for writers of hooks +\label{sec:hook:ref} + + + +In-process hook execution + +An in-process hook is called with arguments of the following form: + + + def myhook(ui, repo, **kwargs): + pass + + +The ui parameter is a ui object. +The repo parameter is a +localrepository object. The +names and values of the **kwargs parameters depend on the +hook being invoked, with the following common features: + + +If a parameter is named node or + parentN, it will contain a hexadecimal changeset ID. + The empty string is used to represent null changeset ID instead + of a string of zeroes. + + +If a parameter is named url, it will contain the URL of + a remote repository, if that can be determined. + + +Boolean-valued parameters are represented as Python + bool objects. + + + +An in-process hook is called without a change to the process's working +directory (unlike external hooks, which are run in the root of the +repository). It must not change the process's working directory, or +it will cause any calls it makes into the Mercurial API to fail. + + +If a hook returns a boolean false value, it is considered to have +succeeded. If it returns a boolean true value or raises an +exception, it is considered to have failed. A useful way to think of +the calling convention is tell me if you fail. + + +Note that changeset IDs are passed into Python hooks as hexadecimal +strings, not the binary hashes that Mercurial's APIs normally use. To +convert a hash from hex to binary, use the +\pymodfunc{mercurial.node}{bin} function. + + + + +External hook execution + +An external hook is passed to the shell of the user running Mercurial. +Features of that shell, such as variable substitution and command +redirection, are available. The hook is run in the root directory of +the repository (unlike in-process hooks, which are run in the same +directory that Mercurial was run in). + + +Hook parameters are passed to the hook as environment variables. Each +environment variable's name is converted in upper case and prefixed +with the string HG_. For example, if the name of a +parameter is node, the name of the environment variable +representing that parameter will be HG_NODE. + + +A boolean parameter is represented as the string 1 for +true, 0 for false. If an environment variable is +named HG_NODE, HG_PARENT1 or HG_PARENT2, it +contains a changeset ID represented as a hexadecimal string. The +empty string is used to represent null changeset ID instead of a +string of zeroes. If an environment variable is named +HG_URL, it will contain the URL of a remote repository, if +that can be determined. + + +If a hook exits with a status of zero, it is considered to have +succeeded. If it exits with a non-zero status, it is considered to +have failed. + + + + +Finding out where changesets come from + +A hook that involves the transfer of changesets between a local +repository and another may be able to find out information about the +far side. Mercurial knows how changes are being +transferred, and in many cases where they are being transferred +to or from. + + + +Sources of changesets +\label{sec:hook:sources} + + +Mercurial will tell a hook what means are, or were, used to transfer +changesets between repositories. This is provided by Mercurial in a +Python parameter named source, or an environment variable named +HG_SOURCE. + + + +serve: Changesets are transferred to or from a remote + repository over http or ssh. + + +pull: Changesets are being transferred via a pull from + one repository into another. + + +push: Changesets are being transferred via a push from + one repository into another. + + +bundle: Changesets are being transferred to or from a + bundle. + + + + + +Where changes are going&emdash;remote repository URLs +\label{sec:hook:url} + + +When possible, Mercurial will tell a hook the location of the far +side of an activity that transfers changeset data between +repositories. This is provided by Mercurial in a Python parameter +named url, or an environment variable named HG_URL. + + +This information is not always known. If a hook is invoked in a +repository that is being served via http or ssh, Mercurial cannot tell +where the remote repository is, but it may know where the client is +connecting from. In such cases, the URL will take one of the +following forms: + + +remote:ssh:ip-address&emdash;remote ssh client, at + the given IP address. + + +remote:http:ip-address&emdash;remote http client, at + the given IP address. If the client is using SSL, this will be of + the form remote:https:ip-address. + + +Empty&emdash;no information could be discovered about the remote + client. + + + + + + + +Hook reference + + +<literal role="hook">changegroup</literal>&emdash;after remote changesets added +\label{sec:hook:changegroup} + + +This hook is run after a group of pre-existing changesets has been +added to the repository, for example via a hg pull or +hg unbundle. This hook is run once per operation that added one +or more changesets. This is in contrast to the incoming hook, +which is run once per changeset, regardless of whether the changesets +arrive in a group. + + +Some possible uses for this hook include kicking off an automated +build or test of the added changesets, updating a bug database, or +notifying subscribers that a repository contains new changes. + + +Parameters to this hook: + + +node: A changeset ID. The changeset ID of the first + changeset in the group that was added. All changesets between this + and \index{tags!tip}tip, inclusive, were added by + a single hg pull, hg push or hg unbundle. + + +source: A string. The source of these changes. See + section for details. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: incoming (section ), +prechangegroup (section ), +pretxnchangegroup (section ) + + + + +<literal role="hook">commit</literal>&emdash;after a new changeset is created +\label{sec:hook:commit} + + +This hook is run after a new changeset has been created. + + +Parameters to this hook: + + +node: A changeset ID. The changeset ID of the newly + committed changeset. + + +parent1: A changeset ID. The changeset ID of the first + parent of the newly committed changeset. + + +parent2: A changeset ID. The changeset ID of the second + parent of the newly committed changeset. + + + +See also: precommit (section ), +pretxncommit (section ) + + + + +<literal role="hook">incoming</literal>&emdash;after one remote changeset is added +\label{sec:hook:incoming} + + +This hook is run after a pre-existing changeset has been added to the +repository, for example via a hg push. If a group of changesets +was added in a single operation, this hook is called once for each +added changeset. + + +You can use this hook for the same purposes as the changegroup +hook (section ); it's simply more convenient +sometimes to run a hook once per group of changesets, while other +times it's handier once per changeset. + + +Parameters to this hook: + + +node: A changeset ID. The ID of the newly added + changeset. + + +source: A string. The source of these changes. See + section for details. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: changegroup (section ) prechangegroup (section ), pretxnchangegroup (section ) + + + + +<literal role="hook">outgoing</literal>&emdash;after changesets are propagated +\label{sec:hook:outgoing} + + +This hook is run after a group of changesets has been propagated out +of this repository, for example by a hg push or hg bundle +command. + + +One possible use for this hook is to notify administrators that +changes have been pulled. + + +Parameters to this hook: + + +node: A changeset ID. The changeset ID of the first + changeset of the group that was sent. + + +source: A string. The source of the of the operation + (see section ). If a remote client pulled + changes from this repository, source will be + serve. If the client that obtained changes from this + repository was local, source will be bundle, + pull, or push, depending on the operation the + client performed. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: preoutgoing (section ) + + + + +<literal role="hook">prechangegroup</literal>&emdash;before starting to add remote changesets +\label{sec:hook:prechangegroup} + + +This controlling hook is run before Mercurial begins to add a group of +changesets from another repository. + + +This hook does not have any information about the changesets to be +added, because it is run before transmission of those changesets is +allowed to begin. If this hook fails, the changesets will not be +transmitted. + + +One use for this hook is to prevent external changes from being added +to a repository. For example, you could use this to freeze a +server-hosted branch temporarily or permanently so that users cannot +push to it, while still allowing a local administrator to modify the +repository. + + +Parameters to this hook: + + +source: A string. The source of these changes. See + section for details. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: changegroup (section ), +incoming (section ), , +pretxnchangegroup (section ) + + + + +<literal role="hook">precommit</literal>&emdash;before starting to commit a changeset +\label{sec:hook:precommit} + + +This hook is run before Mercurial begins to commit a new changeset. +It is run before Mercurial has any of the metadata for the commit, +such as the files to be committed, the commit message, or the commit +date. + + +One use for this hook is to disable the ability to commit new +changesets, while still allowing incoming changesets. Another is to +run a build or test, and only allow the commit to begin if the build +or test succeeds. + + +Parameters to this hook: + + +parent1: A changeset ID. The changeset ID of the first + parent of the working directory. + + +parent2: A changeset ID. The changeset ID of the second + parent of the working directory. + + +If the commit proceeds, the parents of the working directory will +become the parents of the new changeset. + + +See also: commit (section ), +pretxncommit (section ) + + + + +<literal role="hook">preoutgoing</literal>&emdash;before starting to propagate changesets +\label{sec:hook:preoutgoing} + + +This hook is invoked before Mercurial knows the identities of the +changesets to be transmitted. + + +One use for this hook is to prevent changes from being transmitted to +another repository. + + +Parameters to this hook: + + +source: A string. The source of the operation that is + attempting to obtain changes from this repository (see + section ). See the documentation for the + source parameter to the outgoing hook, in + section , for possible values of this + parameter. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: outgoing (section ) + + + + +<literal role="hook">pretag</literal>&emdash;before tagging a changeset +\label{sec:hook:pretag} + + +This controlling hook is run before a tag is created. If the hook +succeeds, creation of the tag proceeds. If the hook fails, the tag is +not created. + + +Parameters to this hook: + + +local: A boolean. Whether the tag is local to this + repository instance (i.e. stored in .hg/localtags) or + managed by Mercurial (stored in .hgtags). + + +node: A changeset ID. The ID of the changeset to be tagged. + + +tag: A string. The name of the tag to be created. + + + +If the tag to be created is revision-controlled, the precommit +and pretxncommit hooks (sections +and ) will also be run. + + +See also: tag (section ) + + +\subsection{pretxnchangegroup&emdash;before completing addition of + remote changesets} +\label{sec:hook:pretxnchangegroup} + + +This controlling hook is run before a transaction&emdash;that manages the +addition of a group of new changesets from outside the +repository&emdash;completes. If the hook succeeds, the transaction +completes, and all of the changesets become permanent within this +repository. If the hook fails, the transaction is rolled back, and +the data for the changesets is erased. + + +This hook can access the metadata associated with the almost-added +changesets, but it should not do anything permanent with this data. +It must also not modify the working directory. + + +While this hook is running, if other Mercurial processes access this +repository, they will be able to see the almost-added changesets as if +they are permanent. This may lead to race conditions if you do not +take steps to avoid them. + + +This hook can be used to automatically vet a group of changesets. If +the hook fails, all of the changesets are rejected when the +transaction rolls back. + + +Parameters to this hook: + + +node: A changeset ID. The changeset ID of the first + changeset in the group that was added. All changesets between this + and \index{tags!tip}tip, inclusive, were added by + a single hg pull, hg push or hg unbundle. + + +source: A string. The source of these changes. See + section for details. + + +url: A URL. The location of the remote repository, if + known. See section for more information. + + + +See also: changegroup (section ), +incoming (section ), +prechangegroup (section ) + + + + +<literal role="hook">pretxncommit</literal>&emdash;before completing commit of new changeset +\label{sec:hook:pretxncommit} + + +This controlling hook is run before a transaction&emdash;that manages a new +commit&emdash;completes. If the hook succeeds, the transaction completes +and the changeset becomes permanent within this repository. If the +hook fails, the transaction is rolled back, and the commit data is +erased. + + +This hook can access the metadata associated with the almost-new +changeset, but it should not do anything permanent with this data. It +must also not modify the working directory. + + +While this hook is running, if other Mercurial processes access this +repository, they will be able to see the almost-new changeset as if it +is permanent. This may lead to race conditions if you do not take +steps to avoid them. + + +Parameters to this hook: + + +node: A changeset ID. The changeset ID of the newly + committed changeset. + + +parent1: A changeset ID. The changeset ID of the first + parent of the newly committed changeset. + + +parent2: A changeset ID. The changeset ID of the second + parent of the newly committed changeset. + + + +See also: precommit (section ) + + + + +<literal role="hook">preupdate</literal>&emdash;before updating or merging working directory +\label{sec:hook:preupdate} + + +This controlling hook is run before an update or merge of the working +directory begins. It is run only if Mercurial's normal pre-update +checks determine that the update or merge can proceed. If the hook +succeeds, the update or merge may proceed; if it fails, the update or +merge does not start. + + +Parameters to this hook: + + +parent1: A changeset ID. The ID of the parent that the + working directory is to be updated to. If the working directory is + being merged, it will not change this parent. + + +parent2: A changeset ID. Only set if the working + directory is being merged. The ID of the revision that the working + directory is being merged with. + + + +See also: update (section ) + + + + +<literal role="hook">tag</literal>&emdash;after tagging a changeset +\label{sec:hook:tag} + + +This hook is run after a tag has been created. + + +Parameters to this hook: + + +local: A boolean. Whether the new tag is local to this + repository instance (i.e. stored in .hg/localtags) or + managed by Mercurial (stored in .hgtags). + + +node: A changeset ID. The ID of the changeset that was + tagged. + + +tag: A string. The name of the tag that was created. + + + +If the created tag is revision-controlled, the commit hook +(section ) is run before this hook. + + +See also: pretag (section ) + + + + +<literal role="hook">update</literal>&emdash;after updating or merging working directory +\label{sec:hook:update} + + +This hook is run after an update or merge of the working directory +completes. Since a merge can fail (if the external hgmerge +command fails to resolve conflicts in a file), this hook communicates +whether the update or merge completed cleanly. + + + +error: A boolean. Indicates whether the update or + merge completed successfully. + + +parent1: A changeset ID. The ID of the parent that the + working directory was updated to. If the working directory was + merged, it will not have changed this parent. + + +parent2: A changeset ID. Only set if the working + directory was merged. The ID of the revision that the working + directory was merged with. + + + +See also: preupdate (section ) + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch11-template.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch11-template.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,689 @@ + + + +Customising the output of Mercurial +\label{chap:template} + +Mercurial provides a powerful mechanism to let you control how it +displays information. The mechanism is based on templates. You can +use templates to generate specific output for a single command, or to +customise the entire appearance of the built-in web interface. + + +Using precanned output styles +\label{sec:style} + +Packaged with Mercurial are some output styles that you can use +immediately. A style is simply a precanned template that someone +wrote and installed somewhere that Mercurial can find. + +Before we take a look at Mercurial's bundled styles, let's review its +normal output. + + + +This is somewhat informative, but it takes up a lot of space&emdash;five +lines of output per changeset. The compact style reduces +this to three lines, presented in a sparse manner. + + + +The changelog style hints at the expressive power of +Mercurial's templating engine. This style attempts to follow the GNU +Project's changelog guidelinesweb:changelog. + + + + + +You will not be shocked to learn that Mercurial's default output style +is named default. + + + +Setting a default style + +You can modify the output style that Mercurial will use for every +command by editing your /.hgrc\ file, naming the style you would +prefer to use. + + + + [ui] + style = compact + + + +If you write a style of your own, you can use it by either providing +the path to your style file, or copying your style file into a +location where Mercurial can find it (typically the templates +subdirectory of your Mercurial install directory). + + + + + +Commands that support styles and templates + +All of Mercurial's log-like commands let you use styles +and templates: hg incoming, hg log, hg outgoing, and +hg tip. + + +As I write this manual, these are so far the only commands that +support styles and templates. Since these are the most important +commands that need customisable output, there has been little pressure +from the Mercurial user community to add style and template support to +other commands. + + + + +The basics of templating + +At its simplest, a Mercurial template is a piece of text. Some of the +text never changes, while other parts are expanded, or replaced +with new text, when necessary. + + +Before we continue, let's look again at a simple example of +Mercurial's normal output. + + + + + +Now, let's run the same command, but using a template to change its +output. + + + + + +The example above illustrates the simplest possible template; it's +just a piece of static text, printed once for each changeset. The + option to the hg log command tells +Mercurial to use the given text as the template when printing each +changeset. + + +Notice that the template string above ends with the text +\n. This is an escape sequence, telling Mercurial +to print a newline at the end of each template item. If you omit this +newline, Mercurial will run each piece of output together. See +section for more details of escape sequences. + + +A template that prints a fixed string of text all the time isn't very +useful; let's try something a bit more complex. + + + + + +As you can see, the string {desc} in the template has been +replaced in the output with the description of each changeset. Every +time Mercurial finds text enclosed in curly braces ({ +and \texttt{}}), it will try to replace the braces and text with +the expansion of whatever is inside. To print a literal curly brace, +you must escape it, as described in section . + + + + +Common template keywords +\label{sec:template:keyword} + + +You can start writing simple templates immediately using the keywords +below. + + + +author: String. The unmodified author of the changeset. + + +branches: String. The name of the branch on which + the changeset was committed. Will be empty if the branch name was + default. + + +date: Date information. The date when the changeset + was committed. This is not human-readable; you must pass it + through a filter that will render it appropriately. See + section for more information on filters. + The date is expressed as a pair of numbers. The first number is a + Unix UTC timestamp (seconds since January 1, 1970); the second is + the offset of the committer's timezone from UTC, in seconds. + + +desc: String. The text of the changeset description. + + +files: List of strings. All files modified, added, or + removed by this changeset. + + +file_adds: List of strings. Files added by this + changeset. + + +file_dels: List of strings. Files removed by this + changeset. + + +node: String. The changeset identification hash, as a + 40-character hexadecimal string. + + +parents: List of strings. The parents of the + changeset. + + +rev: Integer. The repository-local changeset revision + number. + + +tags: List of strings. Any tags associated with the + changeset. + + + +A few simple experiments will show us what to expect when we use these +keywords; you can see the results in +figure . + + + + + Template keywords in use + \label{fig:template:keywords} + + + +As we noted above, the date keyword does not produce human-readable +output, so we must treat it specially. This involves using a +filter, about which more in section . + + + + + + + +Escape sequences +\label{sec:template:escape} + + +Mercurial's templating engine recognises the most commonly used escape +sequences in strings. When it sees a backslash (\) +character, it looks at the following character and substitutes the two +characters with a single replacement, as described below. + + + +\textbackslash\textbackslash: Backslash, \, + ASCII 134. + + +\textbackslash n: Newline, ASCII 12. + + +\textbackslash r: Carriage return, ASCII 15. + + +\textbackslash t: Tab, ASCII 11. + + +\textbackslash v: Vertical tab, ASCII 13. + + +\textbackslash {: Open curly brace, {, ASCII 173. + + +\textbackslash }: Close curly brace, }, ASCII 175. + + + +As indicated above, if you want the expansion of a template to contain +a literal \, {, or { character, you +must escape it. + + + + +Filtering keywords to change their results +\label{sec:template:filter} + + +Some of the results of template expansion are not immediately easy to +use. Mercurial lets you specify an optional chain of filters +to modify the result of expanding a keyword. You have already seen a +common filter, isodate, in action above, to make a +date readable. + + +Below is a list of the most commonly used filters that Mercurial +supports. While some filters can be applied to any text, others can +only be used in specific circumstances. The name of each filter is +followed first by an indication of where it can be used, then a +description of its effect. + + + +addbreaks: Any text. Add an XHTML <br/> + tag before the end of every line except the last. For example, + foo\nbar becomes foo<br/>\nbar. + + +age: date keyword. Render the + age of the date, relative to the current time. Yields a string like + 10 minutes. + + +basename: Any text, but most useful for the + files keyword and its relatives. Treat the text as a + path, and return the basename. For example, foo/bar/baz + becomes baz. + + +date: date keyword. Render a date + in a similar format to the Unix date command, but with + timezone included. Yields a string like + Mon Sep 04 15:13:13 2006 -0700. + + +domain: Any text, but most useful for the + author keyword. Finds the first string that looks like + an email address, and extract just the domain component. For + example, Bryan O'Sullivan <bos@serpentine.com> becomes + serpentine.com. + + +email: Any text, but most useful for the + author keyword. Extract the first string that looks like + an email address. For example, + Bryan O'Sullivan <bos@serpentine.com> becomes + bos@serpentine.com. + + +escape: Any text. Replace the special XML/XHTML + characters &, < and > with + XML entities. + + +fill68: Any text. Wrap the text to fit in 68 + columns. This is useful before you pass text through the + tabindent filter, and still want it to fit in an + 80-column fixed-font window. + + +fill76: Any text. Wrap the text to fit in 76 + columns. + + +firstline: Any text. Yield the first line of text, + without any trailing newlines. + + +hgdate: date keyword. Render the + date as a pair of readable numbers. Yields a string like + 1157407993 25200. + + +isodate: date keyword. Render the + date as a text string in ISO 8601 format. Yields a string like + 2006-09-04 15:13:13 -0700. + + +obfuscate: Any text, but most useful for the + author keyword. Yield the input text rendered as a + sequence of XML entities. This helps to defeat some particularly + stupid screen-scraping email harvesting spambots. + + +person: Any text, but most useful for the + author keyword. Yield the text before an email address. + For example, Bryan O'Sullivan <bos@serpentine.com> + becomes Bryan O'Sullivan. + + +rfc822date: date keyword. Render a + date using the same format used in email headers. Yields a string + like Mon, 04 Sep 2006 15:13:13 -0700. + + +short: Changeset hash. Yield the short form + of a changeset hash, i.e. a 12-character hexadecimal string. + + +shortdate: date keyword. Render + the year, month, and day of the date. Yields a string like + 2006-09-04. + + +strip: Any text. Strip all leading and trailing + whitespace from the string. + + +tabindent: Any text. Yield the text, with every line + except the first starting with a tab character. + + +urlescape: Any text. Escape all characters that are + considered special by URL parsers. For example, foo bar + becomes foo%20bar. + + +user: Any text, but most useful for the + author keyword. Return the user portion of an email + address. For example, + Bryan O'Sullivan <bos@serpentine.com> becomes + bos. + + + + + + Template filters in action + \label{fig:template:filters} + + + + + If you try to apply a filter to a piece of data that it cannot + process, Mercurial will fail and print a Python exception. For + example, trying to run the output of the desc keyword + into the isodate filter is not a good idea. + + + + +Combining filters + +It is easy to combine filters to yield output in the form you would +like. The following chain of filters tidies up a description, then +makes sure that it fits cleanly into 68 columns, then indents it by a +further 8 characters (at least on Unix-like systems, where a tab is +conventionally 8 characters wide). + + + + + +Note the use of \t (a tab character) in the template to +force the first line to be indented; this is necessary since +tabindent indents all lines except the first. + + +Keep in mind that the order of filters in a chain is significant. The +first filter is applied to the result of the keyword; the second to +the result of the first filter; and so on. For example, using +fill68|tabindent gives very different results from +tabindent|fill68. + + + + + + +From templates to styles + +A command line template provides a quick and simple way to format some +output. Templates can become verbose, though, and it's useful to be +able to give a template a name. A style file is a template with a +name, stored in a file. + + +More than that, using a style file unlocks the power of Mercurial's +templating engine in ways that are not possible using the command line + option. + + + +The simplest of style files + +Our simple style file contains just one line: + + + + + +This tells Mercurial, if you're printing a changeset, use the text +on the right as the template. + + + + +Style file syntax + +The syntax rules for a style file are simple. + + + +The file is processed one line at a time. + + + + +Leading and trailing white space are ignored. + + + + +Empty lines are skipped. + + + + +If a line starts with either of the characters # or + ;, the entire line is treated as a comment, and skipped + as if empty. + + + + +A line starts with a keyword. This must start with an + alphabetic character or underscore, and can subsequently contain any + alphanumeric character or underscore. (In regexp notation, a + keyword must match [A-Za-z_][A-Za-z0-9_]*.) + + + + +The next element must be an = character, which can + be preceded or followed by an arbitrary amount of white space. + + + + +If the rest of the line starts and ends with matching quote + characters (either single or double quote), it is treated as a + template body. + + + + +If the rest of the line does not start with a quote + character, it is treated as the name of a file; the contents of this + file will be read and used as a template body. + + + + + + +Style files by example + +To illustrate how to write a style file, we will construct a few by +example. Rather than provide a complete style file and walk through +it, we'll mirror the usual process of developing a style file by +starting with something very simple, and walking through a series of +successively more complete examples. + + + +Identifying mistakes in style files + +If Mercurial encounters a problem in a style file you are working on, +it prints a terse error message that, once you figure out what it +means, is actually quite useful. + + + + + +Notice that broken.style attempts to define a +changeset keyword, but forgets to give any content for it. +When instructed to use this style file, Mercurial promptly complains. + + + + + +This error message looks intimidating, but it is not too hard to +follow. + + + +The first component is simply Mercurial's way of saying I am + giving up. + + + abort: broken.style:1: parse error + + + + + +Next comes the name of the style file that contains the error. + + + abort: broken.style:1: parse error + + + + + +Following the file name is the line number where the error was + encountered. + + + abort: broken.style:1: parse error + + + + + +Finally, a description of what went wrong. + + + abort: broken.style:1: parse error + + + The description of the problem is not always clear (as in this + case), but even when it is cryptic, it is almost always trivial to + visually inspect the offending line in the style file and see what + is wrong. + + + + + +Uniquely identifying a repository + +If you would like to be able to identify a Mercurial repository +fairly uniquely using a short string as an identifier, you can +use the first revision in the repository. + +This is not guaranteed to be unique, but it is nevertheless useful in +many cases. + + +It will not work in a completely empty repository, because such + a repository does not have a revision zero. + + +Neither will it work in the (extremely rare) case where a + repository is a merge of two or more formerly independent + repositories, and you still have those repositories around. + + +Here are some uses to which you could put this identifier: + + +As a key into a table for a database that manages repositories + on a server. + + +As half of a {repository ID, revision ID} tuple. + Save this information away when you run an automated build or other + activity, so that you can replay the build later if necessary. + + + + + +Mimicking Subversion's output + +Let's try to emulate the default output format used by another +revision control tool, Subversion. + + + +Since Subversion's output style is fairly simple, it is easy to +copy-and-paste a hunk of its output into a file, and replace the text +produced above by Subversion with the template values we'd like to see +expanded. + + + +There are a few small ways in which this template deviates from the +output produced by Subversion. + + +Subversion prints a readable date (the \texttt{Wed, 27 Sep + 2006} in the example output above) in parentheses. Mercurial's + templating engine does not provide a way to display a date in this + format without also printing the time and time zone. + + +We emulate Subversion's printing of separator lines full of + - characters by ending the template with such a line. + We use the templating engine's header keyword to print a + separator line as the first line of output (see below), thus + achieving similar output to Subversion. + + +Subversion's output includes a count in the header of the number + of lines in the commit message. We cannot replicate this in + Mercurial; the templating engine does not currently provide a filter + that counts the number of lines the template generates. + + +It took me no more than a minute or two of work to replace literal +text from an example of Subversion's output with some keywords and +filters to give the template above. The style file simply refers to +the template. + + + +We could have included the text of the template file directly in the +style file by enclosing it in quotes and replacing the newlines with +\n sequences, but it would have made the style file too +difficult to read. Readability is a good guide when you're trying to +decide whether some text belongs in a style file, or in a template +file that the style file points to. If the style file will look too +big or cluttered if you insert a literal piece of text, drop it into a +template instead. + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch12-mq.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch12-mq.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,1309 @@ + + + +Managing change with Mercurial Queues +\label{chap:mq} + + +The patch management problem +\label{sec:mq:patch-mgmt} + +Here is a common scenario: you need to install a software package from +source, but you find a bug that you must fix in the source before you +can start using the package. You make your changes, forget about the +package for a while, and a few months later you need to upgrade to a +newer version of the package. If the newer version of the package +still has the bug, you must extract your fix from the older source +tree and apply it against the newer version. This is a tedious task, +and it's easy to make mistakes. + +This is a simple case of the patch management problem. You have +an upstream source tree that you can't change; you need to make +some local changes on top of the upstream tree; and you'd like to be +able to keep those changes separate, so that you can apply them to +newer versions of the upstream source. + +The patch management problem arises in many situations. Probably the +most visible is that a user of an open source software project will +contribute a bug fix or new feature to the project's maintainers in the +form of a patch. + +Distributors of operating systems that include open source software +often need to make changes to the packages they distribute so that +they will build properly in their environments. + +When you have few changes to maintain, it is easy to manage a single +patch using the standard diff and patch programs +(see section for a discussion of these tools). +Once the number of changes grows, it starts to make sense to maintain +patches as discrete chunks of work, so that for example a single +patch will contain only one bug fix (the patch might modify several +files, but it's doing only one thing), and you may have a number +of such patches for different bugs you need fixed and local changes +you require. In this situation, if you submit a bug fix patch to the +upstream maintainers of a package and they include your fix in a +subsequent release, you can simply drop that single patch when you're +updating to the newer release. + +Maintaining a single patch against an upstream tree is a little +tedious and error-prone, but not difficult. However, the complexity +of the problem grows rapidly as the number of patches you have to +maintain increases. With more than a tiny number of patches in hand, +understanding which ones you have applied and maintaining them moves +from messy to overwhelming. + +Fortunately, Mercurial includes a powerful extension, Mercurial Queues +(or simply MQ), that massively simplifies the patch management +problem. + + + + +The prehistory of Mercurial Queues +\label{sec:mq:history} + + +During the late 1990s, several Linux kernel developers started to +maintain patch series that modified the behaviour of the Linux +kernel. Some of these series were focused on stability, some on +feature coverage, and others were more speculative. + + +The sizes of these patch series grew rapidly. In 2002, Andrew Morton +published some shell scripts he had been using to automate the task of +managing his patch queues. Andrew was successfully using these +scripts to manage hundreds (sometimes thousands) of patches on top of +the Linux kernel. + + + +A patchwork quilt +\label{sec:mq:quilt} + + +In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the +approach of Andrew's scripts and published a tool called patchwork +quilt web:quilt, or simply quilt +(see gruenbacher:2005 for a paper describing it). Because +quilt substantially automated patch management, it rapidly gained a +large following among open source software developers. + + +Quilt manages a stack of patches on top of a directory tree. +To begin, you tell quilt to manage a directory tree, and tell it which +files you want to manage; it stores away the names and contents of +those files. To fix a bug, you create a new patch (using a single +command), edit the files you need to fix, then refresh the patch. + + +The refresh step causes quilt to scan the directory tree; it updates +the patch with all of the changes you have made. You can create +another patch on top of the first, which will track the changes +required to modify the tree from tree with one patch applied to +tree with two patches applied. + + +You can change which patches are applied to the tree. If you +pop a patch, the changes made by that patch will vanish from the +directory tree. Quilt remembers which patches you have popped, +though, so you can push a popped patch again, and the directory +tree will be restored to contain the modifications in the patch. Most +importantly, you can run the refresh command at any time, and the +topmost applied patch will be updated. This means that you can, at +any time, change both which patches are applied and what +modifications those patches make. + + +Quilt knows nothing about revision control tools, so it works equally +well on top of an unpacked tarball or a Subversion working copy. + + + + +From patchwork quilt to Mercurial Queues +\label{sec:mq:quilt-mq} + + +In mid-2005, Chris Mason took the features of quilt and wrote an +extension that he called Mercurial Queues, which added quilt-like +behaviour to Mercurial. + + +The key difference between quilt and MQ is that quilt knows nothing +about revision control systems, while MQ is integrated into +Mercurial. Each patch that you push is represented as a Mercurial +changeset. Pop a patch, and the changeset goes away. + + +Because quilt does not care about revision control tools, it is still +a tremendously useful piece of software to know about for situations +where you cannot use Mercurial and MQ. + + + + + +The huge advantage of MQ + +I cannot overstate the value that MQ offers through the unification of +patches and revision control. + + +A major reason that patches have persisted in the free software and +open source world&emdash;in spite of the availability of increasingly +capable revision control tools over the years&emdash;is the agility +they offer. + + +Traditional revision control tools make a permanent, irreversible +record of everything that you do. While this has great value, it's +also somewhat stifling. If you want to perform a wild-eyed +experiment, you have to be careful in how you go about it, or you risk +leaving unneeded&emdash;or worse, misleading or destabilising&emdash;traces of +your missteps and errors in the permanent revision record. + + +By contrast, MQ's marriage of distributed revision control with +patches makes it much easier to isolate your work. Your patches live +on top of normal revision history, and you can make them disappear or +reappear at will. If you don't like a patch, you can drop it. If a +patch isn't quite as you want it to be, simply fix it&emdash;as many times +as you need to, until you have refined it into the form you desire. + + +As an example, the integration of patches with revision control makes +understanding patches and debugging their effects&emdash;and their +interplay with the code they're based on&emdash;enormously easier. +Since every applied patch has an associated changeset, you can use +hg log filename to see which changesets and patches +affected a file. You can use the bisect command to +binary-search through all changesets and applied patches to see where +a bug got introduced or fixed. You can use the hg annotate +command to see which changeset or patch modified a particular line of +a source file. And so on. + + + + +Understanding patches +\label{sec:mq:patch} + + +Because MQ doesn't hide its patch-oriented nature, it is helpful to +understand what patches are, and a little about the tools that work +with them. + + +The traditional Unix diff command compares two files, and +prints a list of differences between them. The patch command +understands these differences as modifications to make to a +file. Take a look at figure for a simple example of +these commands in action. + + + + + Simple uses of the diff and \command{patch commands} + \label{ex:mq:diff} + + + +The type of file that diff generates (and patch +takes as input) is called a patch or a diff; there is no +difference between a patch and a diff. (We'll use the term patch, +since it's more commonly used.) + + +A patch file can start with arbitrary text; the patch +command ignores this text, but MQ uses it as the commit message when +creating changesets. To find the beginning of the patch content, +patch searches for the first line that starts with the +string diff -. + + +MQ works with unified diffs (patch can accept several +other diff formats, but MQ doesn't). A unified diff contains two +kinds of header. The file header describes the file being +modified; it contains the name of the file to modify. When +patch sees a new file header, it looks for a file with that +name to start modifying. + + +After the file header comes a series of hunks. Each hunk +starts with a header; this identifies the range of line numbers within +the file that the hunk should modify. Following the header, a hunk +starts and ends with a few (usually three) lines of text from the +unmodified file; these are called the context for the hunk. If +there's only a small amount of context between successive hunks, +diff doesn't print a new hunk header; it just runs the hunks +together, with a few lines of context between modifications. + + +Each line of context begins with a space character. Within the hunk, +a line that begins with - means remove this line, +while a line that begins with + means insert this +line. For example, a line that is modified is represented by one +deletion and one insertion. + + +We will return to some of the more subtle aspects of patches later (in +section ), but you should have enough information +now to use MQ. + + + + +Getting started with Mercurial Queues +\label{sec:mq:start} + + +Because MQ is implemented as an extension, you must explicitly enable +before you can use it. (You don't need to download anything; MQ ships +with the standard Mercurial distribution.) To enable MQ, edit your +~/.hgrc file, and add the lines in figure . + + + + + [extensions] + hgext.mq = + + + \label{ex:mq:config} + Contents to add to ~/.hgrc to enable the MQ extension + + + +Once the extension is enabled, it will make a number of new commands +available. To verify that the extension is working, you can use +hg help to see if the qinit command is now available; see +the example in figure . + + + + + How to verify that MQ is enabled + \label{ex:mq:enabled} + + + +You can use MQ with any Mercurial repository, and its commands +only operate within that repository. To get started, simply prepare +the repository using the qinit command (see +figure ). This command creates an empty directory +called .hg/patches, where MQ will keep its metadata. As +with many Mercurial commands, the qinit command prints nothing +if it succeeds. + + + + + Preparing a repository for use with MQ + \label{ex:mq:qinit} + + + + + + Creating a new patch + \label{ex:mq:qnew} + + + + +Creating a new patch + +To begin work on a new patch, use the qnew command. This +command takes one argument, the name of the patch to create. MQ will +use this as the name of an actual file in the .hg/patches +directory, as you can see in figure . + + +Also newly present in the .hg/patches directory are two +other files, series and status. The +series file lists all of the patches that MQ knows about +for this repository, with one patch per line. Mercurial uses the +status file for internal book-keeping; it tracks all of the +patches that MQ has applied in this repository. + + + + You may sometimes want to edit the series file by hand; + for example, to change the sequence in which some patches are + applied. However, manually editing the status file is + almost always a bad idea, as it's easy to corrupt MQ's idea of what + is happening. + + + +Once you have created your new patch, you can edit files in the +working directory as you usually would. All of the normal Mercurial +commands, such as hg diff and hg annotate, work exactly as +they did before. + + + + +Refreshing a patch + +When you reach a point where you want to save your work, use the +qrefresh command (figure ) to update the patch +you are working on. This command folds the changes you have made in +the working directory into your patch, and updates its corresponding +changeset to contain those changes. + + + + + Refreshing a patch + \label{ex:mq:qrefresh} + + + +You can run qrefresh as often as you like, so it's a good way +to checkpoint your work. Refresh your patch at an opportune +time; try an experiment; and if the experiment doesn't work out, +hg revert your modifications back to the last time you refreshed. + + + + + Refresh a patch many times to accumulate changes + \label{ex:mq:qrefresh2} + + + + + +Stacking and tracking patches + +Once you have finished working on a patch, or need to work on another, +you can use the qnew command again to create a new patch. +Mercurial will apply this patch on top of your existing patch. See +figure for an example. Notice that the patch +contains the changes in our prior patch as part of its context (you +can see this more clearly in the output of hg annotate). + + + + + Stacking a second patch on top of the first + \label{ex:mq:qnew2} + + + +So far, with the exception of qnew and qrefresh, we've +been careful to only use regular Mercurial commands. However, MQ +provides many commands that are easier to use when you are thinking +about patches, as illustrated in figure : + + + +The qseries command lists every patch that MQ knows + about in this repository, from oldest to newest (most recently + created). + + +The qapplied command lists every patch that MQ has + applied in this repository, again from oldest to newest (most + recently applied). + + + + + + \caption{Understanding the patch stack with qseries and + qapplied} + \label{ex:mq:qseries} + + + + + +Manipulating the patch stack + +The previous discussion implied that there must be a difference +between known and applied patches, and there is. MQ can +manage a patch without it being applied in the repository. + + +An applied patch has a corresponding changeset in the +repository, and the effects of the patch and changeset are visible in +the working directory. You can undo the application of a patch using +the qpop command. MQ still knows about, or manages, a +popped patch, but the patch no longer has a corresponding changeset in +the repository, and the working directory does not contain the changes +made by the patch. Figure illustrates the +difference between applied and tracked patches. + + + + + XXX add text + Applied and unapplied patches in the MQ patch stack + \label{fig:mq:stack} + + + +You can reapply an unapplied, or popped, patch using the qpush +command. This creates a new changeset to correspond to the patch, and +the patch's changes once again become present in the working +directory. See figure for examples of qpop +and qpush in action. Notice that once we have popped a patch +or two patches, the output of qseries remains the same, while +that of qapplied has changed. + + + + + Modifying the stack of applied patches + \label{ex:mq:qpop} + + + + + +Pushing and popping many patches + +While qpush and qpop each operate on a single patch at +a time by default, you can push and pop many patches in one go. The + option to qpush causes it to push all +unapplied patches, while the option to qpop +causes it to pop all applied patches. (For some more ways to push and +pop many patches, see section below.) + + + + + Pushing all unapplied patches + \label{ex:mq:qpush-a} + + + + + +Safety checks, and overriding them + +Several MQ commands check the working directory before they do +anything, and fail if they find any modifications. They do this to +ensure that you won't lose any changes that you have made, but not yet +incorporated into a patch. Figure illustrates this; +the qnew command will not create a new patch if there are +outstanding changes, caused in this case by the hg add of +file3. + + + + + Forcibly creating a patch + \label{ex:mq:add} + + + +Commands that check the working directory all take an I know what +I'm doing option, which is always named . The exact +meaning of depends on the command. For example, +hg qnew will incorporate any outstanding +changes into the new patch it creates, but +hg qpop will revert modifications to any +files affected by the patch that it is popping. Be sure to read the +documentation for a command's option before you use it! + + + + +Working on several patches at once + +The qrefresh command always refreshes the topmost +applied patch. This means that you can suspend work on one patch (by +refreshing it), pop or push to make a different patch the top, and +work on that patch for a while. + + +Here's an example that illustrates how you can use this ability. +Let's say you're developing a new feature as two patches. The first +is a change to the core of your software, and the second&emdash;layered on +top of the first&emdash;changes the user interface to use the code you just +added to the core. If you notice a bug in the core while you're +working on the UI patch, it's easy to fix the core. Simply +qrefresh the UI patch to save your in-progress changes, and +qpop down to the core patch. Fix the core bug, +qrefresh the core patch, and qpush back to the UI +patch to continue where you left off. + + + + + +More about patches +\label{sec:mq:adv-patch} + + +MQ uses the GNU patch command to apply patches, so it's +helpful to know a few more detailed aspects of how patch +works, and about patches themselves. + + + +The strip count + +If you look at the file headers in a patch, you will notice that the +pathnames usually have an extra component on the front that isn't +present in the actual path name. This is a holdover from the way that +people used to generate patches (people still do this, but it's +somewhat rare with modern revision control tools). + + +Alice would unpack a tarball, edit her files, then decide that she +wanted to create a patch. So she'd rename her working directory, +unpack the tarball again (hence the need for the rename), and use the + and options to diff to +recursively generate a patch between the unmodified directory and the +modified one. The result would be that the name of the unmodified +directory would be at the front of the left-hand path in every file +header, and the name of the modified directory would be at the front +of the right-hand path. + + +Since someone receiving a patch from the Alices of the net would be +unlikely to have unmodified and modified directories with exactly the +same names, the patch command has a +option that indicates the number of leading path name components to +strip when trying to apply a patch. This number is called the +strip count. + + +An option of -p1 means use a strip count of one. If +patch sees a file name foo/bar/baz in a file +header, it will strip foo and try to patch a file named +bar/baz. (Strictly speaking, the strip count refers to the +number of path separators (and the components that go with them +) to strip. A strip count of one will turn foo/bar into +bar, but /foo/bar (notice the extra leading +slash) into foo/bar.) + + +The standard strip count for patches is one; almost all patches +contain one leading path name component that needs to be stripped. +Mercurial's hg diff command generates path names in this form, +and the hg import command and MQ expect patches to have a strip +count of one. + + +If you receive a patch from someone that you want to add to your patch +queue, and the patch needs a strip count other than one, you cannot +just qimport the patch, because qimport does not yet +have a -p option (see issue 311). Your best bet is to +qnew a patch of your own, then use patch -pN +to apply their patch, followed by hg addremove to pick up any +files added or removed by the patch, followed by qrefresh. +This complexity may become unnecessary; see issue 311 for details. + + +Strategies for applying a patch + + +When patch applies a hunk, it tries a handful of +successively less accurate strategies to try to make the hunk apply. +This falling-back technique often makes it possible to take a patch +that was generated against an old version of a file, and apply it +against a newer version of that file. + + +First, patch tries an exact match, where the line numbers, +the context, and the text to be modified must apply exactly. If it +cannot make an exact match, it tries to find an exact match for the +context, without honouring the line numbering information. If this +succeeds, it prints a line of output saying that the hunk was applied, +but at some offset from the original line number. + + +If a context-only match fails, patch removes the first and +last lines of the context, and tries a reduced context-only +match. If the hunk with reduced context succeeds, it prints a message +saying that it applied the hunk with a fuzz factor (the number +after the fuzz factor indicates how many lines of context +patch had to trim before the patch applied). + + +When neither of these techniques works, patch prints a +message saying that the hunk in question was rejected. It saves +rejected hunks (also simply called rejects) to a file with the +same name, and an added .rej extension. It also saves an +unmodified copy of the file with a .orig extension; the +copy of the file without any extensions will contain any changes made +by hunks that did apply cleanly. If you have a patch that +modifies foo with six hunks, and one of them fails to +apply, you will have: an unmodified foo.orig, a +foo.rej containing one hunk, and foo, containing +the changes made by the five successful hunks. + + + + +Some quirks of patch representation + +There are a few useful things to know about how patch works +with files. + + +This should already be obvious, but patch cannot + handle binary files. + + +Neither does it care about the executable bit; it creates new + files as readable, but not executable. + + +patch treats the removal of a file as a diff between + the file to be removed and the empty file. So your idea of I + deleted this file looks like every line of this file was + deleted in a patch. + + +It treats the addition of a file as a diff between the empty + file and the file to be added. So in a patch, your idea of I + added this file looks like every line of this file was added. + + +It treats a renamed file as the removal of the old name, and the + addition of the new name. This means that renamed files have a big + footprint in patches. (Note also that Mercurial does not currently + try to infer when files have been renamed or copied in a patch.) + + +patch cannot represent empty files, so you cannot use + a patch to represent the notion I added this empty file to the + tree. + + + + +Beware the fuzz + +While applying a hunk at an offset, or with a fuzz factor, will often +be completely successful, these inexact techniques naturally leave +open the possibility of corrupting the patched file. The most common +cases typically involve applying a patch twice, or at an incorrect +location in the file. If patch or qpush ever +mentions an offset or fuzz factor, you should make sure that the +modified files are correct afterwards. + + +It's often a good idea to refresh a patch that has applied with an +offset or fuzz factor; refreshing the patch generates new context +information that will make it apply cleanly. I say often, not +always, because sometimes refreshing a patch will make it fail to +apply against a different revision of the underlying files. In some +cases, such as when you're maintaining a patch that must sit on top of +multiple versions of a source tree, it's acceptable to have a patch +apply with some fuzz, provided you've verified the results of the +patching process in such cases. + + + + +Handling rejection + +If qpush fails to apply a patch, it will print an error +message and exit. If it has left .rej files behind, it is +usually best to fix up the rejected hunks before you push more patches +or do any further work. + + +If your patch used to apply cleanly, and no longer does because +you've changed the underlying code that your patches are based on, +Mercurial Queues can help; see section for details. + + +Unfortunately, there aren't any great techniques for dealing with +rejected hunks. Most often, you'll need to view the .rej +file and edit the target file, applying the rejected hunks by hand. + + +If you're feeling adventurous, Neil Brown, a Linux kernel hacker, +wrote a tool called wiggle web:wiggle, which is more +vigorous than patch in its attempts to make a patch apply. + + +Another Linux kernel hacker, Chris Mason (the author of Mercurial +Queues), wrote a similar tool called +mpatch web:mpatch, which takes a simple approach to +automating the application of hunks rejected by patch. The +mpatch command can help with four common reasons that a hunk +may be rejected: + + + +The context in the middle of a hunk has changed. + + +A hunk is missing some context at the beginning or end. + + +A large hunk might apply better&emdash;either entirely or in + part&emdash;if it was broken up into smaller hunks. + + +A hunk removes lines with slightly different content than those + currently present in the file. + + + +If you use wiggle or mpatch, you should be doubly +careful to check your results when you're done. In fact, +mpatch enforces this method of double-checking the tool's +output, by automatically dropping you into a merge program when it has +done its job, so that you can verify its work and finish off any +remaining merges. + + + + + +Getting the best performance out of MQ +\label{sec:mq:perf} + + +MQ is very efficient at handling a large number of patches. I ran +some performance experiments in mid-2006 for a talk that I gave at the +2006 EuroPython conference web:europython. I used as my data +set the Linux 2.6.17-mm1 patch series, which consists of 1,738 +patches. I applied these on top of a Linux kernel repository +containing all 27,472 revisions between Linux 2.6.12-rc2 and Linux +2.6.17. + + +On my old, slow laptop, I was able to +hg qpush all 1,738 patches in 3.5 minutes, +and hg qpop them all in 30 seconds. (On a +newer laptop, the time to push all patches dropped to two minutes.) I +could qrefresh one of the biggest patches (which made 22,779 +lines of changes to 287 files) in 6.6 seconds. + + +Clearly, MQ is well suited to working in large trees, but there are a +few tricks you can use to get the best performance of it. + + +First of all, try to batch operations together. Every time you +run qpush or qpop, these commands scan the working +directory once to make sure you haven't made some changes and then +forgotten to run qrefresh. On a small tree, the time that +this scan takes is unnoticeable. However, on a medium-sized tree +(containing tens of thousands of files), it can take a second or more. + + +The qpush and qpop commands allow you to push and pop +multiple patches at a time. You can identify the destination +patch that you want to end up at. When you qpush with a +destination specified, it will push patches until that patch is at the +top of the applied stack. When you qpop to a destination, MQ +will pop patches until the destination patch is at the top. + + +You can identify a destination patch using either the name of the +patch, or by number. If you use numeric addressing, patches are +counted from zero; this means that the first patch is zero, the second +is one, and so on. + + + + +Updating your patches when the underlying code changes +\label{sec:mq:merge} + + +It's common to have a stack of patches on top of an underlying +repository that you don't modify directly. If you're working on +changes to third-party code, or on a feature that is taking longer to +develop than the rate of change of the code beneath, you will often +need to sync up with the underlying code, and fix up any hunks in your +patches that no longer apply. This is called rebasing your +patch series. + + +The simplest way to do this is to hg qpop +your patches, then hg pull changes into the underlying +repository, and finally hg qpush your +patches again. MQ will stop pushing any time it runs across a patch +that fails to apply during conflicts, allowing you to fix your +conflicts, qrefresh the affected patch, and continue pushing +until you have fixed your entire stack. + + +This approach is easy to use and works well if you don't expect +changes to the underlying code to affect how well your patches apply. +If your patch stack touches code that is modified frequently or +invasively in the underlying repository, however, fixing up rejected +hunks by hand quickly becomes tiresome. + + +It's possible to partially automate the rebasing process. If your +patches apply cleanly against some revision of the underlying repo, MQ +can use this information to help you to resolve conflicts between your +patches and a different revision. + + +The process is a little involved. + + +To begin, hg qpush -a all of your patches on top of + the revision where you know that they apply cleanly. + + +Save a backup copy of your patch directory using + hg qsave . This prints + the name of the directory that it has saved the patches in. It will + save the patches to a directory called + .hg/patches.N, where N is a small + integer. It also commits a save changeset on top of your + applied patches; this is for internal book-keeping, and records the + states of the series and status files. + + +Use hg pull to bring new changes into the underlying + repository. (Don't run hg pull -u; see below for why.) + + +Update to the new tip revision, using + hg update to override the patches you + have pushed. + + +Merge all patches using \hgcmdargs{qpush}{ + }. The option to qpush + tells MQ to perform a three-way merge if the patch fails to apply. + + + +During the hg qpush , each patch in the +series file is applied normally. If a patch applies with +fuzz or rejects, MQ looks at the queue you qsaved, and +performs a three-way merge with the corresponding changeset. This +merge uses Mercurial's normal merge machinery, so it may pop up a GUI +merge tool to help you to resolve problems. + + +When you finish resolving the effects of a patch, MQ refreshes your +patch based on the result of the merge. + + +At the end of this process, your repository will have one extra head +from the old patch queue, and a copy of the old patch queue will be in +.hg/patches.N. You can remove the extra head using +hg qpop patches.N +or hg strip. You can delete .hg/patches.N once +you are sure that you no longer need it as a backup. + + + + +Identifying patches + +MQ commands that work with patches let you refer to a patch either by +using its name or by a number. By name is obvious enough; pass the +name foo.patch to qpush, for example, and it will +push patches until foo.patch is applied. + + +As a shortcut, you can refer to a patch using both a name and a +numeric offset; foo.patch-2 means two patches before +foo.patch, while bar.patch+4 means four patches +after bar.patch. + + +Referring to a patch by index isn't much different. The first patch +printed in the output of qseries is patch zero (yes, it's one +of those start-at-zero counting systems); the second is patch one; and +so on. + + +MQ also makes it easy to work with patches when you are using normal +Mercurial commands. Every command that accepts a changeset ID will +also accept the name of an applied patch. MQ augments the tags +normally in the repository with an eponymous one for each applied +patch. In addition, the special tags \index{tags!special tag + names!qbase}qbase and \index{tags!special tag + names!qtip}qtip identify the bottom-most and +topmost applied patches, respectively. + + +These additions to Mercurial's normal tagging capabilities make +dealing with patches even more of a breeze. + + +Want to patchbomb a mailing list with your latest series of + changes? + + + hg email qbase:qtip + + + (Don't know what patchbombing is? See + section .) + + +Need to see all of the patches since foo.patch that + have touched files in a subdirectory of your tree? + + + hg log -r foo.patch:qtip subdir + + + + +Because MQ makes the names of patches available to the rest of +Mercurial through its normal internal tag machinery, you don't need to +type in the entire name of a patch when you want to identify it by +name. + + + + + Using MQ's tag features to work with patches + \label{ex:mq:id} + + + +Another nice consequence of representing patch names as tags is that +when you run the hg log command, it will display a patch's name +as a tag, simply as part of its normal output. This makes it easy to +visually distinguish applied patches from underlying normal +revisions. Figure shows a few normal Mercurial +commands in use with applied patches. + + + + +Useful things to know about + +There are a number of aspects of MQ usage that don't fit tidily into +sections of their own, but that are good to know. Here they are, in +one place. + + + +Normally, when you qpop a patch and qpush it + again, the changeset that represents the patch after the pop/push + will have a different identity than the changeset that + represented the hash beforehand. See + section for information as to why this is. + + +It's not a good idea to hg merge changes from another + branch with a patch changeset, at least if you want to maintain the + patchiness of that changeset and changesets below it on the + patch stack. If you try to do this, it will appear to succeed, but + MQ will become confused. + + + + + +Managing patches in a repository +\label{sec:mq:repo} + + +Because MQ's .hg/patches directory resides outside a +Mercurial repository's working directory, the underlying Mercurial +repository knows nothing about the management or presence of patches. + + +This presents the interesting possibility of managing the contents of +the patch directory as a Mercurial repository in its own right. This +can be a useful way to work. For example, you can work on a patch for +a while, qrefresh it, then hg commit the current state of +the patch. This lets you roll back to that version of the patch +later on. + + +You can then share different versions of the same patch stack among +multiple underlying repositories. I use this when I am developing a +Linux kernel feature. I have a pristine copy of my kernel sources for +each of several CPU architectures, and a cloned repository under each +that contains the patches I am working on. When I want to test a +change on a different architecture, I push my current patches to the +patch repository associated with that kernel tree, pop and push all of +my patches, and build and test that kernel. + + +Managing patches in a repository makes it possible for multiple +developers to work on the same patch series without colliding with +each other, all on top of an underlying source base that they may or +may not control. + + + +MQ support for patch repositories + +MQ helps you to work with the .hg/patches directory as a +repository; when you prepare a repository for working with patches +using qinit, you can pass the option to +create the .hg/patches directory as a Mercurial repository. + + + + If you forget to use the option, you can simply go + into the .hg/patches directory at any time and run + hg init. Don't forget to add an entry for the + status file to the .hgignore file, though + + + (hg qinit does this for you + automatically); you really don't want to manage the + status file. + + + +As a convenience, if MQ notices that the .hg/patches +directory is a repository, it will automatically hg add every +patch that you create and import. + + +MQ provides a shortcut command, qcommit, that runs +hg commit in the .hg/patches directory. This saves +some bothersome typing. + + +Finally, as a convenience to manage the patch directory, you can +define the alias mq on Unix systems. For example, on Linux +systems using the bash shell, you can include the following +snippet in your ~/.bashrc. + + + + alias mq=`hg -R $(hg root)/.hg/patches' + + + +You can then issue commands of the form mq pull from +the main repository. + + + + +A few things to watch out for + +MQ's support for working with a repository full of patches is limited +in a few small respects. + + +MQ cannot automatically detect changes that you make to the patch +directory. If you hg pull, manually edit, or hg update +changes to patches or the series file, you will have to +hg qpop and then +hg qpush in the underlying repository to +see those changes show up there. If you forget to do this, you can +confuse MQ's idea of which patches are applied. + + + + + +Third party tools for working with patches +\label{sec:mq:tools} + + +Once you've been working with patches for a while, you'll find +yourself hungry for tools that will help you to understand and +manipulate the patches you're dealing with. + + +The diffstat command web:diffstat generates a +histogram of the modifications made to each file in a patch. It +provides a good way to get a sense of a patch&emdash;which files it +affects, and how much change it introduces to each file and as a +whole. (I find that it's a good idea to use diffstat's + option as a matter of course, as otherwise it +will try to do clever things with prefixes of file names that +inevitably confuse at least me.) + + + + + The diffstat, \command{filterdiff, and lsdiff commands} + \label{ex:mq:tools} + + + +The patchutils package web:patchutils is invaluable. +It provides a set of small utilities that follow the Unix +philosophy; each does one useful thing with a patch. The +patchutils command I use most is filterdiff, which +extracts subsets from a patch file. For example, given a patch that +modifies hundreds of files across dozens of directories, a single +invocation of filterdiff can generate a smaller patch that +only touches files whose names match a particular glob pattern. See +section for another example. + + + + +Good ways to work with patches + +Whether you are working on a patch series to submit to a free software +or open source project, or a series that you intend to treat as a +sequence of regular changesets when you're done, you can use some +simple techniques to keep your work well organised. + + +Give your patches descriptive names. A good name for a patch might be +rework-device-alloc.patch, because it will immediately give +you a hint what the purpose of the patch is. Long names shouldn't be +a problem; you won't be typing the names often, but you will be +running commands like qapplied and qtop over and over. +Good naming becomes especially important when you have a number of +patches to work with, or if you are juggling a number of different +tasks and your patches only get a fraction of your attention. + + +Be aware of what patch you're working on. Use the qtop +command and skim over the text of your patches frequently&emdash;for +example, using hg tip )&emdash;to be sure of where +you stand. I have several times worked on and qrefreshed a +patch other than the one I intended, and it's often tricky to migrate +changes into the right patch after making them in the wrong one. + + +For this reason, it is very much worth investing a little time to +learn how to use some of the third-party tools I described in +section , particularly diffstat and +filterdiff. The former will give you a quick idea of what +changes your patch is making, while the latter makes it easy to splice +hunks selectively out of one patch and into another. + + + + +MQ cookbook + + +Manage <quote>trivial</quote> patches + +Because the overhead of dropping files into a new Mercurial repository +is so low, it makes a lot of sense to manage patches this way even if +you simply want to make a few changes to a source tarball that you +downloaded. + + +Begin by downloading and unpacking the source tarball, +and turning it into a Mercurial repository. + + + +Continue by creating a patch stack and making your changes. + + + +Let's say a few weeks or months pass, and your package author releases +a new version. First, bring their changes into the repository. + +The pipeline starting with hg locate above deletes all files in +the working directory, so that hg commit's + option can actually tell which files have +really been removed in the newer version of the source. + + +Finally, you can apply your patches on top of the new tree. + + + + + +Combining entire patches +\label{sec:mq:combine} + + +MQ provides a command, qfold that lets you combine entire +patches. This folds the patches you name, in the order you name +them, into the topmost applied patch, and concatenates their +descriptions onto the end of its description. The patches that you +fold must be unapplied before you fold them. + + +The order in which you fold patches matters. If your topmost applied +patch is foo, and you qfold bar and +quux into it, you will end up with a patch that has the same +effect as if you applied first foo, then bar, +followed by quux. + + + + +Merging part of one patch into another + +Merging part of one patch into another is more difficult than +combining entire patches. + + +If you want to move changes to entire files, you can use +filterdiff's and + options to choose the modifications to snip +out of one patch, concatenating its output onto the end of the patch +you want to merge into. You usually won't need to modify the patch +you've merged the changes from. Instead, MQ will report some rejected +hunks when you qpush it (from the hunks you moved into the +other patch), and you can simply qrefresh the patch to drop +the duplicate hunks. + + +If you have a patch that has multiple hunks modifying a file, and you +only want to move a few of those hunks, the job becomes more messy, +but you can still partly automate it. Use lsdiff -nvv to +print some metadata about the patch. + + + +This command prints three different kinds of number: + + +(in the first column) a file number to identify each file + modified in the patch; + + +(on the next line, indented) the line number within a modified + file where a hunk starts; and + + +(on the same line) a hunk number to identify that hunk. + + + +You'll have to use some visual inspection, and reading of the patch, +to identify the file and hunk numbers you'll want, but you can then +pass them to to filterdiff's +and options, to select exactly the file +and hunk you want to extract. + + +Once you have this hunk, you can concatenate it onto the end of your +destination patch and continue with the remainder of +section . + + + + + +Differences between quilt and MQ + +If you are already familiar with quilt, MQ provides a similar command +set. There are a few differences in the way that it works. + + +You will already have noticed that most quilt commands have MQ +counterparts that simply begin with a q. The exceptions +are quilt's add and remove commands, the +counterparts for which are the normal Mercurial hg add and +hg remove commands. There is no MQ equivalent of the quilt +edit command. + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch13-mq-collab.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch13-mq-collab.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,499 @@ + + + +Advanced uses of Mercurial Queues +\label{chap:mq-collab} + +While it's easy to pick up straightforward uses of Mercurial Queues, +use of a little discipline and some of MQ's less frequently used +capabilities makes it possible to work in complicated development +environments. + +In this chapter, I will use as an example a technique I have used to +manage the development of an Infiniband device driver for the Linux +kernel. The driver in question is large (at least as drivers go), +with 25,000 lines of code spread across 35 source files. It is +maintained by a small team of developers. + +While much of the material in this chapter is specific to Linux, the +same principles apply to any code base for which you're not the +primary owner, and upon which you need to do a lot of development. + + +The problem of many targets + +The Linux kernel changes rapidly, and has never been internally +stable; developers frequently make drastic changes between releases. +This means that a version of the driver that works well with a +particular released version of the kernel will not even compile +correctly against, typically, any other version. + +To maintain a driver, we have to keep a number of distinct versions of +Linux in mind. + +One target is the main Linux kernel development tree. + Maintenance of the code is in this case partly shared by other + developers in the kernel community, who make drive-by + modifications to the driver as they develop and refine kernel + subsystems. + +We also maintain a number of backports to older versions of + the Linux kernel, to support the needs of customers who are running + older Linux distributions that do not incorporate our drivers. (To + backport a piece of code is to modify it to work in an older + version of its target environment than the version it was developed + for.) + +Finally, we make software releases on a schedule that is + necessarily not aligned with those used by Linux distributors and + kernel developers, so that we can deliver new features to customers + without forcing them to upgrade their entire kernels or + distributions. + + + + +Tempting approaches that don't work well + +There are two standard ways to maintain a piece of software that +has to target many different environments. + + +The first is to maintain a number of branches, each intended for a +single target. The trouble with this approach is that you must +maintain iron discipline in the flow of changes between repositories. +A new feature or bug fix must start life in a pristine repository, +then percolate out to every backport repository. Backport changes are +more limited in the branches they should propagate to; a backport +change that is applied to a branch where it doesn't belong will +probably stop the driver from compiling. + + +The second is to maintain a single source tree filled with conditional +statements that turn chunks of code on or off depending on the +intended target. Because these ifdefs are not allowed in the +Linux kernel tree, a manual or automatic process must be followed to +strip them out and yield a clean tree. A code base maintained in this +fashion rapidly becomes a rat's nest of conditional blocks that are +difficult to understand and maintain. + + +Neither of these approaches is well suited to a situation where you +don't own the canonical copy of a source tree. In the case of a +Linux driver that is distributed with the standard kernel, Linus's +tree contains the copy of the code that will be treated by the world +as canonical. The upstream version of my driver can be modified +by people I don't know, without me even finding out about it until +after the changes show up in Linus's tree. + + +These approaches have the added weakness of making it difficult to +generate well-formed patches to submit upstream. + + +In principle, Mercurial Queues seems like a good candidate to manage a +development scenario such as the above. While this is indeed the +case, MQ contains a few added features that make the job more +pleasant. + + +\section{Conditionally applying patches with + guards} + + +Perhaps the best way to maintain sanity with so many targets is to be +able to choose specific patches to apply for a given situation. MQ +provides a feature called guards (which originates with quilt's +guards command) that does just this. To start off, let's +create a simple repository for experimenting in. + +This gives us a tiny repository that contains two patches that don't +have any dependencies on each other, because they touch different files. + + +The idea behind conditional application is that you can tag a +patch with a guard, which is simply a text string of your +choosing, then tell MQ to select specific guards to use when applying +patches. MQ will then either apply, or skip over, a guarded patch, +depending on the guards that you have selected. + + +A patch can have an arbitrary number of guards; +each one is positive (apply this patch if this guard is +selected) or negative (skip this patch if this guard is +selected). A patch with no guards is always applied. + + + + + +Controlling the guards on a patch + +The qguard command lets you determine which guards should +apply to a patch, or display the guards that are already in effect. +Without any arguments, it displays the guards on the current topmost +patch. + +To set a positive guard on a patch, prefix the name of the guard with +a +. + +To set a negative guard on a patch, prefix the name of the guard with +a -. + + + + + The qguard command sets the guards on a patch; it + doesn't modify them. What this means is that if you run + hg qguard +a +b on a patch, then hg qguard +c on + the same patch, the only guard that will be set on it + afterwards is +c. + + + +Mercurial stores guards in the series file; the form in +which they are stored is easy both to understand and to edit by hand. +(In other words, you don't have to use the qguard command if +you don't want to; it's okay to simply edit the series +file.) + + + + + +Selecting the guards to use + +The qselect command determines which guards are active at a +given time. The effect of this is to determine which patches MQ will +apply the next time you run qpush. It has no other effect; in +particular, it doesn't do anything to patches that are already +applied. + + +With no arguments, the qselect command lists the guards +currently in effect, one per line of output. Each argument is treated +as the name of a guard to apply. + +In case you're interested, the currently selected guards are stored in +the guards file. + +We can see the effect the selected guards have when we run +qpush. + + + +A guard cannot start with a + or - +character. The name of a guard must not contain white space, but most +other characters are acceptable. If you try to use a guard with an +invalid name, MQ will complain: + +Changing the selected guards changes the patches that are applied. + +You can see in the example below that negative guards take precedence +over positive guards. + + + + + +MQ's rules for applying patches + +The rules that MQ uses when deciding whether to apply a patch +are as follows. + + +A patch that has no guards is always applied. + + +If the patch has any negative guard that matches any currently + selected guard, the patch is skipped. + + +If the patch has any positive guard that matches any currently + selected guard, the patch is applied. + + +If the patch has positive or negative guards, but none matches + any currently selected guard, the patch is skipped. + + + + + +Trimming the work environment + +In working on the device driver I mentioned earlier, I don't apply the +patches to a normal Linux kernel tree. Instead, I use a repository +that contains only a snapshot of the source files and headers that are +relevant to Infiniband development. This repository is 1% the size +of a kernel repository, so it's easier to work with. + + +I then choose a base version on top of which the patches are +applied. This is a snapshot of the Linux kernel tree as of a revision +of my choosing. When I take the snapshot, I record the changeset ID +from the kernel repository in the commit message. Since the snapshot +preserves the shape and content of the relevant parts of the +kernel tree, I can apply my patches on top of either my tiny +repository or a normal kernel tree. + + +Normally, the base tree atop which the patches apply should be a +snapshot of a very recent upstream tree. This best facilitates the +development of patches that can easily be submitted upstream with few +or no modifications. + + + + +Dividing up the <filename role="special">series</filename> file + +I categorise the patches in the series file into a number +of logical groups. Each section of like patches begins with a block +of comments that describes the purpose of the patches that follow. + + +The sequence of patch groups that I maintain follows. The ordering of +these groups is important; I'll describe why after I introduce the +groups. + + +The accepted group. Patches that the development team has + submitted to the maintainer of the Infiniband subsystem, and which + he has accepted, but which are not present in the snapshot that the + tiny repository is based on. These are read only patches, + present only to transform the tree into a similar state as it is in + the upstream maintainer's repository. + + +The rework group. Patches that I have submitted, but that + the upstream maintainer has requested modifications to before he + will accept them. + + +The pending group. Patches that I have not yet submitted to + the upstream maintainer, but which we have finished working on. + These will be read only for a while. If the upstream maintainer + accepts them upon submission, I'll move them to the end of the + accepted group. If he requests that I modify any, I'll move + them to the beginning of the rework group. + + +The in progress group. Patches that are actively being + developed, and should not be submitted anywhere yet. + + +The backport group. Patches that adapt the source tree to + older versions of the kernel tree. + + +The do not ship group. Patches that for some reason should + never be submitted upstream. For example, one such patch might + change embedded driver identification strings to make it easier to + distinguish, in the field, between an out-of-tree version of the + driver and a version shipped by a distribution vendor. + + + +Now to return to the reasons for ordering groups of patches in this +way. We would like the lowest patches in the stack to be as stable as +possible, so that we will not need to rework higher patches due to +changes in context. Putting patches that will never be changed first +in the series file serves this purpose. + + +We would also like the patches that we know we'll need to modify to be +applied on top of a source tree that resembles the upstream tree as +closely as possible. This is why we keep accepted patches around for +a while. + + +The backport and do not ship patches float at the end of the +series file. The backport patches must be applied on top +of all other patches, and the do not ship patches might as well +stay out of harm's way. + + + + +Maintaining the patch series + +In my work, I use a number of guards to control which patches are to +be applied. + + + +Accepted patches are guarded with accepted. I + enable this guard most of the time. When I'm applying the patches + on top of a tree where the patches are already present, I can turn + this patch off, and the patches that follow it will apply cleanly. + + +Patches that are finished, but not yet submitted, have no + guards. If I'm applying the patch stack to a copy of the upstream + tree, I don't need to enable any guards in order to get a reasonably + safe source tree. + + +Those patches that need reworking before being resubmitted are + guarded with rework. + + +For those patches that are still under development, I use + devel. + + +A backport patch may have several guards, one for each version + of the kernel to which it applies. For example, a patch that + backports a piece of code to 2.6.9 will have a 2.6.9 guard. + + +This variety of guards gives me considerable flexibility in +determining what kind of source tree I want to end up with. For most +situations, the selection of appropriate guards is automated during +the build process, but I can manually tune the guards to use for less +common circumstances. + + + +The art of writing backport patches + +Using MQ, writing a backport patch is a simple process. All such a +patch has to do is modify a piece of code that uses a kernel feature +not present in the older version of the kernel, so that the driver +continues to work correctly under that older version. + + +A useful goal when writing a good backport patch is to make your code +look as if it was written for the older version of the kernel you're +targeting. The less obtrusive the patch, the easier it will be to +understand and maintain. If you're writing a collection of backport +patches to avoid the rat's nest effect of lots of +#ifdefs (hunks of source code that are only used +conditionally) in your code, don't introduce version-dependent +#ifdefs into the patches. Instead, write several patches, +each of which makes unconditional changes, and control their +application using guards. + + +There are two reasons to divide backport patches into a distinct +group, away from the regular patches whose effects they modify. +The first is that intermingling the two makes it more difficult to use +a tool like the patchbomb extension to automate the process of +submitting the patches to an upstream maintainer. The second is that +a backport patch could perturb the context in which a subsequent +regular patch is applied, making it impossible to apply the regular +patch cleanly without the earlier backport patch already being +applied. + + + + + +Useful tips for developing with MQ + + +Organising patches in directories + +If you're working on a substantial project with MQ, it's not difficult +to accumulate a large number of patches. For example, I have one +patch repository that contains over 250 patches. + + +If you can group these patches into separate logical categories, you +can if you like store them in different directories; MQ has no +problems with patch names that contain path separators. + + + + +Viewing the history of a patch +\label{mq-collab:tips:interdiff} + + +If you're developing a set of patches over a long time, it's a good +idea to maintain them in a repository, as discussed in +section . If you do so, you'll quickly discover that +using the hg diff command to look at the history of changes to a +patch is unworkable. This is in part because you're looking at the +second derivative of the real code (a diff of a diff), but also +because MQ adds noise to the process by modifying time stamps and +directory names when it updates a patch. + + +However, you can use the extdiff extension, which is bundled +with Mercurial, to turn a diff of two versions of a patch into +something readable. To do this, you will need a third-party package +called patchutils web:patchutils. This provides a +command named interdiff, which shows the differences between +two diffs as a diff. Used on two versions of the same diff, it +generates a diff that represents the diff from the first to the second +version. + + +You can enable the extdiff extension in the usual way, by +adding a line to the extensions section of your /.hgrc. + + + [extensions] + extdiff = + + +The interdiff command expects to be passed the names of two +files, but the extdiff extension passes the program it runs a +pair of directories, each of which can contain an arbitrary number of +files. We thus need a small program that will run interdiff +on each pair of files in these two directories. This program is +available as hg-interdiff in the examples +directory of the source code repository that accompanies this book. + + + +With the hg-interdiff program in your shell's search path, +you can run it as follows, from inside an MQ patch directory: + + + hg extdiff -p hg-interdiff -r A:B my-change.patch + + +Since you'll probably want to use this long-winded command a lot, you +can get hgext to make it available as a normal Mercurial +command, again by editing your /.hgrc. + + + [extdiff] + cmd.interdiff = hg-interdiff + + +This directs hgext to make an interdiff command +available, so you can now shorten the previous invocation of +extdiff to something a little more wieldy. + + + hg interdiff -r A:B my-change.patch + + + + + The interdiff command works well only if the underlying + files against which versions of a patch are generated remain the + same. If you create a patch, modify the underlying files, and then + regenerate the patch, interdiff may not produce useful + output. + + + +The extdiff extension is useful for more than merely improving +the presentation of MQ patches. To read more about it, go to +section . + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/ch14-hgext.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/ch14-hgext.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,539 @@ + + + +Adding functionality with extensions +\label{chap:hgext} + +While the core of Mercurial is quite complete from a functionality +standpoint, it's deliberately shorn of fancy features. This approach +of preserving simplicity keeps the software easy to deal with for both +maintainers and users. + +However, Mercurial doesn't box you in with an inflexible command set: +you can add features to it as extensions (sometimes known as +plugins). We've already discussed a few of these extensions in +earlier chapters. + +Section covers the fetch + extension; this combines pulling new changes and merging them with + local changes into a single command, fetch. + +In chapter , we covered several extensions that + are useful for hook-related functionality: acl adds access + control lists; bugzilla adds integration with the Bugzilla + bug tracking system; and notify sends notification emails on + new changes. + +The Mercurial Queues patch management extension is so invaluable + that it merits two chapters and an appendix all to itself. + Chapter covers the basics; + chapter discusses advanced topics; and + appendix goes into detail on each command. + + +In this chapter, we'll cover some of the other extensions that are +available for Mercurial, and briefly touch on some of the machinery +you'll need to know about if you want to write an extension of your +own. + +In section , we'll discuss the + possibility of huge performance improvements using the + inotify extension. + + + +Improve performance with the <literal role="hg-ext">inotify</literal> extension +\label{sec:hgext:inotify} + + +Are you interested in having some of the most common Mercurial +operations run as much as a hundred times faster? Read on! + + +Mercurial has great performance under normal circumstances. For +example, when you run the hg status command, Mercurial has to +scan almost every directory and file in your repository so that it can +display file status. Many other Mercurial commands need to do the +same work behind the scenes; for example, the hg diff command +uses the status machinery to avoid doing an expensive comparison +operation on files that obviously haven't changed. + + +Because obtaining file status is crucial to good performance, the +authors of Mercurial have optimised this code to within an inch of its +life. However, there's no avoiding the fact that when you run +hg status, Mercurial is going to have to perform at least one +expensive system call for each managed file to determine whether it's +changed since the last time Mercurial checked. For a sufficiently +large repository, this can take a long time. + + +To put a number on the magnitude of this effect, I created a +repository containing 150,000 managed files. I timed hg status +as taking ten seconds to run, even when none of those files had +been modified. + + +Many modern operating systems contain a file notification facility. +If a program signs up to an appropriate service, the operating system +will notify it every time a file of interest is created, modified, or +deleted. On Linux systems, the kernel component that does this is +called inotify. + + +Mercurial's inotify extension talks to the kernel's +inotify component to optimise hg status commands. The +extension has two components. A daemon sits in the background and +receives notifications from the inotify subsystem. It also +listens for connections from a regular Mercurial command. The +extension modifies Mercurial's behaviour so that instead of scanning +the filesystem, it queries the daemon. Since the daemon has perfect +information about the state of the repository, it can respond with a +result instantaneously, avoiding the need to scan every directory and +file in the repository. + + +Recall the ten seconds that I measured plain Mercurial as taking to +run hg status on a 150,000 file repository. With the +inotify extension enabled, the time dropped to 0.1 seconds, a +factor of one hundred faster. + + +Before we continue, please pay attention to some caveats. + + +The inotify extension is Linux-specific. Because it + interfaces directly to the Linux kernel's inotify + subsystem, it does not work on other operating systems. + + +It should work on any Linux distribution that was released after + early 2005. Older distributions are likely to have a kernel that + lacks inotify, or a version of glibc that does not + have the necessary interfacing support. + + +Not all filesystems are suitable for use with the + inotify extension. Network filesystems such as NFS are a + non-starter, for example, particularly if you're running Mercurial + on several systems, all mounting the same network filesystem. The + kernel's inotify system has no way of knowing about changes + made on another system. Most local filesystems (e.g. ext3, XFS, + ReiserFS) should work fine. + + + +The inotify extension is not yet shipped with Mercurial as of +May 2007, so it's a little more involved to set up than other +extensions. But the performance improvement is worth it! + + +The extension currently comes in two parts: a set of patches to the +Mercurial source code, and a library of Python bindings to the +inotify subsystem. + + + There are two Python inotify binding libraries. One + of them is called pyinotify, and is packaged by some Linux + distributions as python-inotify. This is not the + one you'll need, as it is too buggy and inefficient to be practical. + + +To get going, it's best to already have a functioning copy of +Mercurial installed. + + + If you follow the instructions below, you'll be replacing and + overwriting any existing installation of Mercurial that you might + already have, using the latest bleeding edge Mercurial code. + Don't say you weren't warned! + + + +Clone the Python inotify binding repository. Build and + install it. + + + hg clone http://hg.kublai.com/python/inotify + cd inotify + python setup.py build --force + sudo python setup.py install --skip-build + + + + +Clone the crew Mercurial repository. Clone the + inotify patch repository so that Mercurial Queues will be + able to apply patches to your cope of the crew repository. + + + hg clone http://hg.intevation.org/mercurial/crew + hg clone crew inotify + hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches + + + + +Make sure that you have the Mercurial Queues extension, + mq, enabled. If you've never used MQ, read + section to get started quickly. + + +Go into the inotify repo, and apply all of the + inotify patches using the option to + the qpush command. + + + cd inotify + hg qpush -a + + + If you get an error message from qpush, you should not + continue. Instead, ask for help. + + +Build and install the patched version of Mercurial. + + + python setup.py build --force + sudo python setup.py install --skip-build + + + +Once you've build a suitably patched version of Mercurial, all you +need to do to enable the inotify extension is add an entry to +your /.hgrc. + + + [extensions] + inotify = + + +When the inotify extension is enabled, Mercurial will +automatically and transparently start the status daemon the first time +you run a command that needs status in a repository. It runs one +status daemon per repository. + + +The status daemon is started silently, and runs in the background. If +you look at a list of running processes after you've enabled the +inotify extension and run a few commands in different +repositories, you'll thus see a few hg processes sitting +around, waiting for updates from the kernel and queries from +Mercurial. + + +The first time you run a Mercurial command in a repository when you +have the inotify extension enabled, it will run with about the +same performance as a normal Mercurial command. This is because the +status daemon needs to perform a normal status scan so that it has a +baseline against which to apply later updates from the kernel. +However, every subsequent command that does any kind of status +check should be noticeably faster on repositories of even fairly +modest size. Better yet, the bigger your repository is, the greater a +performance advantage you'll see. The inotify daemon makes +status operations almost instantaneous on repositories of all sizes! + + +If you like, you can manually start a status daemon using the +inserve command. This gives you slightly finer +control over how the daemon ought to run. This command will of course +only be available when the inotify extension is enabled. + + +When you're using the inotify extension, you should notice +no difference at all in Mercurial's behaviour, with the sole +exception of status-related commands running a whole lot faster than +they used to. You should specifically expect that commands will not +print different output; neither should they give different results. +If either of these situations occurs, please report a bug. + + + + +Flexible diff support with the <literal role="hg-ext">extdiff</literal> extension +\label{sec:hgext:extdiff} + + +Mercurial's built-in hg diff command outputs plaintext unified +diffs. + +If you would like to use an external tool to display modifications, +you'll want to use the extdiff extension. This will let you +use, for example, a graphical diff tool. + + +The extdiff extension is bundled with Mercurial, so it's easy +to set up. In the extensions section of your /.hgrc, +simply add a one-line entry to enable the extension. + + + [extensions] + extdiff = + + +This introduces a command named extdiff, which by +default uses your system's diff command to generate a +unified diff in the same form as the built-in hg diff command. + +The result won't be exactly the same as with the built-in hg diff +variations, because the output of diff varies from one +system to another, even when passed the same options. + + +As the making snapshot lines of output above imply, the +extdiff command works by creating two snapshots of +your source tree. The first snapshot is of the source revision; the +second, of the target revision or working directory. The +extdiff command generates these snapshots in a +temporary directory, passes the name of each directory to an external +diff viewer, then deletes the temporary directory. For efficiency, it +only snapshots the directories and files that have changed between the +two revisions. + + +Snapshot directory names have the same base name as your repository. +If your repository path is /quux/bar/foo, then foo +will be the name of each snapshot directory. Each snapshot directory +name has its changeset ID appended, if appropriate. If a snapshot is +of revision a631aca1083f, the directory will be named +foo.a631aca1083f. A snapshot of the working directory won't +have a changeset ID appended, so it would just be foo in +this example. To see what this looks like in practice, look again at +the extdiff example above. Notice that the diff has +the snapshot directory names embedded in its header. + + +The extdiff command accepts two important options. +The option lets you choose a program to +view differences with, instead of diff. With the + option, you can change the options that +extdiff passes to the program (by default, these +options are -Npru, which only make sense if you're +running diff). In other respects, the +extdiff command acts similarly to the built-in +hg diff command: you use the same option names, syntax, and +arguments to specify the revisions you want, the files you want, and +so on. + + +As an example, here's how to run the normal system diff +command, getting it to generate context diffs (using the + option) instead of unified diffs, and five lines of +context instead of the default three (passing 5 as the +argument to the option). + + + +Launching a visual diff tool is just as easy. Here's how to launch +the kdiff3 viewer. + + + hg extdiff -p kdiff3 -o + + + +If your diff viewing command can't deal with directories, you can +easily work around this with a little scripting. For an example of +such scripting in action with the mq extension and the +interdiff command, see +section . + + + +Defining command aliases + +It can be cumbersome to remember the options to both the +extdiff command and the diff viewer you want to use, +so the extdiff extension lets you define new commands +that will invoke your diff viewer with exactly the right options. + + +All you need to do is edit your /.hgrc, and add a section named +extdiff. Inside this section, you can define multiple +commands. Here's how to add a kdiff3 command. Once you've +defined this, you can type hg kdiff3 and the +extdiff extension will run kdiff3 for you. + + + [extdiff] + cmd.kdiff3 = + + +If you leave the right hand side of the definition empty, as above, +the extdiff extension uses the name of the command you defined +as the name of the external program to run. But these names don't +have to be the same. Here, we define a command named \texttt{hg + wibble}, which runs kdiff3. + + + [extdiff] + cmd.wibble = kdiff3 + + + +You can also specify the default options that you want to invoke your +diff viewing program with. The prefix to use is opts., +followed by the name of the command to which the options apply. This +example defines a hg vimdiff command that runs the +vim editor's DirDiff extension. + + + [extdiff] + cmd.vimdiff = vim + opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' + + + + + + +Cherrypicking changes with the <literal role="hg-ext">transplant</literal> extension +\label{sec:hgext:transplant} + + +Need to have a long chat with Brendan about this. + + + + +Send changes via email with the <literal role="hg-ext">patchbomb</literal> extension +\label{sec:hgext:patchbomb} + + +Many projects have a culture of change review, in which people +send their modifications to a mailing list for others to read and +comment on before they commit the final version to a shared +repository. Some projects have people who act as gatekeepers; they +apply changes from other people to a repository to which those others +don't have access. + + +Mercurial makes it easy to send changes over email for review or +application, via its patchbomb extension. The extension is so +namd because changes are formatted as patches, and it's usual to send +one changeset per email message. Sending a long series of changes by +email is thus much like bombing the recipient's inbox, hence +patchbomb. + + +As usual, the basic configuration of the patchbomb extension +takes just one or two lines in your /.hgrc. + + + [extensions] + patchbomb = + + +Once you've enabled the extension, you will have a new command +available, named email. + + +The safest and best way to invoke the email +command is to always run it first with the + option. This will show you what the +command would send, without actually sending anything. Once +you've had a quick glance over the changes and verified that you are +sending the right ones, you can rerun the same command, with the + option removed. + + +The email command accepts the same kind of +revision syntax as every other Mercurial command. For example, this +command will send every revision between 7 and tip, +inclusive. + + + hg email -n 7:tip + + +You can also specify a repository to compare with. If you +provide a repository but no revisions, the email +command will send all revisions in the local repository that are not +present in the remote repository. If you additionally specify +revisions or a branch name (the latter using the + option), this will constrain the +revisions sent. + + +It's perfectly safe to run the email command +without the names of the people you want to send to: if you do this, +it will just prompt you for those values interactively. (If you're +using a Linux or Unix-like system, you should have enhanced +readline-style editing capabilities when entering those +headers, too, which is useful.) + + +When you are sending just one revision, the email +command will by default use the first line of the changeset +description as the subject of the single email message it sends. + + +If you send multiple revisions, the email command +will usually send one message per changeset. It will preface the +series with an introductory message, in which you should describe the +purpose of the series of changes you're sending. + + + +Changing the behaviour of patchbombs + +Not every project has exactly the same conventions for sending changes +in email; the patchbomb extension tries to accommodate a +number of variations through command line options. + + +You can write a subject for the introductory message on the + command line using the option. This + takes one argument, the text of the subject to use. + + +To change the email address from which the messages originate, + use the option. This takes one + argument, the email address to use. + + +The default behaviour is to send unified diffs (see + section for a description of the format), one per + message. You can send a binary bundle instead with the + option. + + +Unified diffs are normally prefaced with a metadata header. You + can omit this, and send unadorned diffs, with the + option. + + +Diffs are normally sent inline, in the same body part as the + description of a patch. This makes it easiest for the largest + number of readers to quote and respond to parts of a diff, as some + mail clients will only quote the first MIME body part in a message. + If you'd prefer to send the description and the diff in separate + body parts, use the option. + + +Instead of sending mail messages, you can write them to an + mbox-format mail folder using the + option. That option takes one + argument, the name of the file to write to. + + +If you would like to add a diffstat-format summary to + each patch, and one to the introductory message, use the + option. The diffstat + command displays a table containing the name of each file patched, + the number of lines affected, and a histogram showing how much each + file is modified. This gives readers a qualitative glance at how + complex a patch is. + + + + + + + + \ No newline at end of file diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/cmdref.tex --- a/fr/cmdref.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,176 +0,0 @@ -\chapter{Command reference} -\label{cmdref} - -\cmdref{add}{add files at the next commit} -\optref{add}{I}{include} -\optref{add}{X}{exclude} -\optref{add}{n}{dry-run} - -\cmdref{diff}{print changes in history or working directory} - -Show differences between revisions for the specified files or -directories, using the unified diff format. For a description of the -unified diff format, see section~\ref{sec:mq:patch}. - -By default, this command does not print diffs for files that Mercurial -considers to contain binary data. To control this behaviour, see the -\hgopt{diff}{-a} and \hgopt{diff}{--git} options. - -\subsection{Options} - -\loptref{diff}{nodates} - -Omit date and time information when printing diff headers. - -\optref{diff}{B}{ignore-blank-lines} - -Do not print changes that only insert or delete blank lines. A line -that contains only whitespace is not considered blank. - -\optref{diff}{I}{include} - -Include files and directories whose names match the given patterns. - -\optref{diff}{X}{exclude} - -Exclude files and directories whose names match the given patterns. - -\optref{diff}{a}{text} - -If this option is not specified, \hgcmd{diff} will refuse to print -diffs for files that it detects as binary. Specifying \hgopt{diff}{-a} -forces \hgcmd{diff} to treat all files as text, and generate diffs for -all of them. - -This option is useful for files that are ``mostly text'' but have a -few embedded NUL characters. If you use it on files that contain a -lot of binary data, its output will be incomprehensible. - -\optref{diff}{b}{ignore-space-change} - -Do not print a line if the only change to that line is in the amount -of white space it contains. - -\optref{diff}{g}{git} - -Print \command{git}-compatible diffs. XXX reference a format -description. - -\optref{diff}{p}{show-function} - -Display the name of the enclosing function in a hunk header, using a -simple heuristic. This functionality is enabled by default, so the -\hgopt{diff}{-p} option has no effect unless you change the value of -the \rcitem{diff}{showfunc} config item, as in the following example. -\interaction{cmdref.diff-p} - -\optref{diff}{r}{rev} - -Specify one or more revisions to compare. The \hgcmd{diff} command -accepts up to two \hgopt{diff}{-r} options to specify the revisions to -compare. - -\begin{enumerate} -\setcounter{enumi}{0} -\item Display the differences between the parent revision of the - working directory and the working directory. -\item Display the differences between the specified changeset and the - working directory. -\item Display the differences between the two specified changesets. -\end{enumerate} - -You can specify two revisions using either two \hgopt{diff}{-r} -options or revision range notation. For example, the two revision -specifications below are equivalent. -\begin{codesample2} - hg diff -r 10 -r 20 - hg diff -r10:20 -\end{codesample2} - -When you provide two revisions, Mercurial treats the order of those -revisions as significant. Thus, \hgcmdargs{diff}{-r10:20} will -produce a diff that will transform files from their contents as of -revision~10 to their contents as of revision~20, while -\hgcmdargs{diff}{-r20:10} means the opposite: the diff that will -transform files from their revision~20 contents to their revision~10 -contents. You cannot reverse the ordering in this way if you are -diffing against the working directory. - -\optref{diff}{w}{ignore-all-space} - -\cmdref{version}{print version and copyright information} - -This command displays the version of Mercurial you are running, and -its copyright license. There are four kinds of version string that -you may see. -\begin{itemize} -\item The string ``\texttt{unknown}''. This version of Mercurial was - not built in a Mercurial repository, and cannot determine its own - version. -\item A short numeric string, such as ``\texttt{1.1}''. This is a - build of a revision of Mercurial that was identified by a specific - tag in the repository where it was built. (This doesn't necessarily - mean that you're running an official release; someone else could - have added that tag to any revision in the repository where they - built Mercurial.) -\item A hexadecimal string, such as ``\texttt{875489e31abe}''. This - is a build of the given revision of Mercurial. -\item A hexadecimal string followed by a date, such as - ``\texttt{875489e31abe+20070205}''. This is a build of the given - revision of Mercurial, where the build repository contained some - local changes that had not been committed. -\end{itemize} - -\subsection{Tips and tricks} - -\subsubsection{Why do the results of \hgcmd{diff} and \hgcmd{status} - differ?} -\label{cmdref:diff-vs-status} - -When you run the \hgcmd{status} command, you'll see a list of files -that Mercurial will record changes for the next time you perform a -commit. If you run the \hgcmd{diff} command, you may notice that it -prints diffs for only a \emph{subset} of the files that \hgcmd{status} -listed. There are two possible reasons for this. - -The first is that \hgcmd{status} prints some kinds of modifications -that \hgcmd{diff} doesn't normally display. The \hgcmd{diff} command -normally outputs unified diffs, which don't have the ability to -represent some changes that Mercurial can track. Most notably, -traditional diffs can't represent a change in whether or not a file is -executable, but Mercurial records this information. - -If you use the \hgopt{diff}{--git} option to \hgcmd{diff}, it will -display \command{git}-compatible diffs that \emph{can} display this -extra information. - -The second possible reason that \hgcmd{diff} might be printing diffs -for a subset of the files displayed by \hgcmd{status} is that if you -invoke it without any arguments, \hgcmd{diff} prints diffs against the -first parent of the working directory. If you have run \hgcmd{merge} -to merge two changesets, but you haven't yet committed the results of -the merge, your working directory has two parents (use \hgcmd{parents} -to see them). While \hgcmd{status} prints modifications relative to -\emph{both} parents after an uncommitted merge, \hgcmd{diff} still -operates relative only to the first parent. You can get it to print -diffs relative to the second parent by specifying that parent with the -\hgopt{diff}{-r} option. There is no way to print diffs relative to -both parents. - -\subsubsection{Generating safe binary diffs} - -If you use the \hgopt{diff}{-a} option to force Mercurial to print -diffs of files that are either ``mostly text'' or contain lots of -binary data, those diffs cannot subsequently be applied by either -Mercurial's \hgcmd{import} command or the system's \command{patch} -command. - -If you want to generate a diff of a binary file that is safe to use as -input for \hgcmd{import}, use the \hgcmd{diff}{--git} option when you -generate the patch. The system \command{patch} command cannot handle -binary patches at all. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/collab.tex --- a/fr/collab.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1118 +0,0 @@ -\chapter{Collaborating with other people} -\label{cha:collab} - -As a completely decentralised tool, Mercurial doesn't impose any -policy on how people ought to work with each other. However, if -you're new to distributed revision control, it helps to have some -tools and examples in mind when you're thinking about possible -workflow models. - -\section{Mercurial's web interface} - -Mercurial has a powerful web interface that provides several -useful capabilities. - -For interactive use, the web interface lets you browse a single -repository or a collection of repositories. You can view the history -of a repository, examine each change (comments and diffs), and view -the contents of each directory and file. - -Also for human consumption, the web interface provides an RSS feed of -the changes in a repository. This lets you ``subscribe'' to a -repository using your favourite feed reader, and be automatically -notified of activity in that repository as soon as it happens. I find -this capability much more convenient than the model of subscribing to -a mailing list to which notifications are sent, as it requires no -additional configuration on the part of whoever is serving the -repository. - -The web interface also lets remote users clone a repository, pull -changes from it, and (when the server is configured to permit it) push -changes back to it. Mercurial's HTTP tunneling protocol aggressively -compresses data, so that it works efficiently even over low-bandwidth -network connections. - -The easiest way to get started with the web interface is to use your -web browser to visit an existing repository, such as the master -Mercurial repository at -\url{http://www.selenic.com/repo/hg?style=gitweb}. - -If you're interested in providing a web interface to your own -repositories, Mercurial provides two ways to do this. The first is -using the \hgcmd{serve} command, which is best suited to short-term -``lightweight'' serving. See section~\ref{sec:collab:serve} below for -details of how to use this command. If you have a long-lived -repository that you'd like to make permanently available, Mercurial -has built-in support for the CGI (Common Gateway Interface) standard, -which all common web servers support. See -section~\ref{sec:collab:cgi} for details of CGI configuration. - -\section{Collaboration models} - -With a suitably flexible tool, making decisions about workflow is much -more of a social engineering challenge than a technical one. -Mercurial imposes few limitations on how you can structure the flow of -work in a project, so it's up to you and your group to set up and live -with a model that matches your own particular needs. - -\subsection{Factors to keep in mind} - -The most important aspect of any model that you must keep in mind is -how well it matches the needs and capabilities of the people who will -be using it. This might seem self-evident; even so, you still can't -afford to forget it for a moment. - -I once put together a workflow model that seemed to make perfect sense -to me, but that caused a considerable amount of consternation and -strife within my development team. In spite of my attempts to explain -why we needed a complex set of branches, and how changes ought to flow -between them, a few team members revolted. Even though they were -smart people, they didn't want to pay attention to the constraints we -were operating under, or face the consequences of those constraints in -the details of the model that I was advocating. - -Don't sweep foreseeable social or technical problems under the rug. -Whatever scheme you put into effect, you should plan for mistakes and -problem scenarios. Consider adding automated machinery to prevent, or -quickly recover from, trouble that you can anticipate. As an example, -if you intend to have a branch with not-for-release changes in it, -you'd do well to think early about the possibility that someone might -accidentally merge those changes into a release branch. You could -avoid this particular problem by writing a hook that prevents changes -from being merged from an inappropriate branch. - -\subsection{Informal anarchy} - -I wouldn't suggest an ``anything goes'' approach as something -sustainable, but it's a model that's easy to grasp, and it works -perfectly well in a few unusual situations. - -As one example, many projects have a loose-knit group of collaborators -who rarely physically meet each other. Some groups like to overcome -the isolation of working at a distance by organising occasional -``sprints''. In a sprint, a number of people get together in a single -location (a company's conference room, a hotel meeting room, that kind -of place) and spend several days more or less locked in there, hacking -intensely on a handful of projects. - -A sprint is the perfect place to use the \hgcmd{serve} command, since -\hgcmd{serve} does not requires any fancy server infrastructure. You -can get started with \hgcmd{serve} in moments, by reading -section~\ref{sec:collab:serve} below. Then simply tell the person -next to you that you're running a server, send the URL to them in an -instant message, and you immediately have a quick-turnaround way to -work together. They can type your URL into their web browser and -quickly review your changes; or they can pull a bugfix from you and -verify it; or they can clone a branch containing a new feature and try -it out. - -The charm, and the problem, with doing things in an ad hoc fashion -like this is that only people who know about your changes, and where -they are, can see them. Such an informal approach simply doesn't -scale beyond a handful people, because each individual needs to know -about $n$ different repositories to pull from. - -\subsection{A single central repository} - -For smaller projects migrating from a centralised revision control -tool, perhaps the easiest way to get started is to have changes flow -through a single shared central repository. This is also the -most common ``building block'' for more ambitious workflow schemes. - -Contributors start by cloning a copy of this repository. They can -pull changes from it whenever they need to, and some (perhaps all) -developers have permission to push a change back when they're ready -for other people to see it. - -Under this model, it can still often make sense for people to pull -changes directly from each other, without going through the central -repository. Consider a case in which I have a tentative bug fix, but -I am worried that if I were to publish it to the central repository, -it might subsequently break everyone else's trees as they pull it. To -reduce the potential for damage, I can ask you to clone my repository -into a temporary repository of your own and test it. This lets us put -off publishing the potentially unsafe change until it has had a little -testing. - -In this kind of scenario, people usually use the \command{ssh} -protocol to securely push changes to the central repository, as -documented in section~\ref{sec:collab:ssh}. It's also usual to -publish a read-only copy of the repository over HTTP using CGI, as in -section~\ref{sec:collab:cgi}. Publishing over HTTP satisfies the -needs of people who don't have push access, and those who want to use -web browsers to browse the repository's history. - -\subsection{Working with multiple branches} - -Projects of any significant size naturally tend to make progress on -several fronts simultaneously. In the case of software, it's common -for a project to go through periodic official releases. A release -might then go into ``maintenance mode'' for a while after its first -publication; maintenance releases tend to contain only bug fixes, not -new features. In parallel with these maintenance releases, one or -more future releases may be under development. People normally use -the word ``branch'' to refer to one of these many slightly different -directions in which development is proceeding. - -Mercurial is particularly well suited to managing a number of -simultaneous, but not identical, branches. Each ``development -direction'' can live in its own central repository, and you can merge -changes from one to another as the need arises. Because repositories -are independent of each other, unstable changes in a development -branch will never affect a stable branch unless someone explicitly -merges those changes in. - -Here's an example of how this can work in practice. Let's say you -have one ``main branch'' on a central server. -\interaction{branching.init} -People clone it, make changes locally, test them, and push them back. - -Once the main branch reaches a release milestone, you can use the -\hgcmd{tag} command to give a permanent name to the milestone -revision. -\interaction{branching.tag} -Let's say some ongoing development occurs on the main branch. -\interaction{branching.main} -Using the tag that was recorded at the milestone, people who clone -that repository at any time in the future can use \hgcmd{update} to -get a copy of the working directory exactly as it was when that tagged -revision was committed. -\interaction{branching.update} - -In addition, immediately after the main branch is tagged, someone can -then clone the main branch on the server to a new ``stable'' branch, -also on the server. -\interaction{branching.clone} - -Someone who needs to make a change to the stable branch can then clone -\emph{that} repository, make their changes, commit, and push their -changes back there. -\interaction{branching.stable} -Because Mercurial repositories are independent, and Mercurial doesn't -move changes around automatically, the stable and main branches are -\emph{isolated} from each other. The changes that you made on the -main branch don't ``leak'' to the stable branch, and vice versa. - -You'll often want all of your bugfixes on the stable branch to show up -on the main branch, too. Rather than rewrite a bugfix on the main -branch, you can simply pull and merge changes from the stable to the -main branch, and Mercurial will bring those bugfixes in for you. -\interaction{branching.merge} -The main branch will still contain changes that are not on the stable -branch, but it will also contain all of the bugfixes from the stable -branch. The stable branch remains unaffected by these changes. - -\subsection{Feature branches} - -For larger projects, an effective way to manage change is to break up -a team into smaller groups. Each group has a shared branch of its -own, cloned from a single ``master'' branch used by the entire -project. People working on an individual branch are typically quite -isolated from developments on other branches. - -\begin{figure}[ht] - \centering - \grafix{feature-branches} - \caption{Feature branches} - \label{fig:collab:feature-branches} -\end{figure} - -When a particular feature is deemed to be in suitable shape, someone -on that feature team pulls and merges from the master branch into the -feature branch, then pushes back up to the master branch. - -\subsection{The release train} - -Some projects are organised on a ``train'' basis: a release is -scheduled to happen every few months, and whatever features are ready -when the ``train'' is ready to leave are allowed in. - -This model resembles working with feature branches. The difference is -that when a feature branch misses a train, someone on the feature team -pulls and merges the changes that went out on that train release into -the feature branch, and the team continues its work on top of that -release so that their feature can make the next release. - -\subsection{The Linux kernel model} - -The development of the Linux kernel has a shallow hierarchical -structure, surrounded by a cloud of apparent chaos. Because most -Linux developers use \command{git}, a distributed revision control -tool with capabilities similar to Mercurial, it's useful to describe -the way work flows in that environment; if you like the ideas, the -approach translates well across tools. - -At the center of the community sits Linus Torvalds, the creator of -Linux. He publishes a single source repository that is considered the -``authoritative'' current tree by the entire developer community. -Anyone can clone Linus's tree, but he is very choosy about whose trees -he pulls from. - -Linus has a number of ``trusted lieutenants''. As a general rule, he -pulls whatever changes they publish, in most cases without even -reviewing those changes. Some of those lieutenants are generally -agreed to be ``maintainers'', responsible for specific subsystems -within the kernel. If a random kernel hacker wants to make a change -to a subsystem that they want to end up in Linus's tree, they must -find out who the subsystem's maintainer is, and ask that maintainer to -take their change. If the maintainer reviews their changes and agrees -to take them, they'll pass them along to Linus in due course. - -Individual lieutenants have their own approaches to reviewing, -accepting, and publishing changes; and for deciding when to feed them -to Linus. In addition, there are several well known branches that -people use for different purposes. For example, a few people maintain -``stable'' repositories of older versions of the kernel, to which they -apply critical fixes as needed. Some maintainers publish multiple -trees: one for experimental changes; one for changes that they are -about to feed upstream; and so on. Others just publish a single -tree. - -This model has two notable features. The first is that it's ``pull -only''. You have to ask, convince, or beg another developer to take a -change from you, because there are almost no trees to which more than -one person can push, and there's no way to push changes into a tree -that someone else controls. - -The second is that it's based on reputation and acclaim. If you're an -unknown, Linus will probably ignore changes from you without even -responding. But a subsystem maintainer will probably review them, and -will likely take them if they pass their criteria for suitability. -The more ``good'' changes you contribute to a maintainer, the more -likely they are to trust your judgment and accept your changes. If -you're well-known and maintain a long-lived branch for something Linus -hasn't yet accepted, people with similar interests may pull your -changes regularly to keep up with your work. - -Reputation and acclaim don't necessarily cross subsystem or ``people'' -boundaries. If you're a respected but specialised storage hacker, and -you try to fix a networking bug, that change will receive a level of -scrutiny from a network maintainer comparable to a change from a -complete stranger. - -To people who come from more orderly project backgrounds, the -comparatively chaotic Linux kernel development process often seems -completely insane. It's subject to the whims of individuals; people -make sweeping changes whenever they deem it appropriate; and the pace -of development is astounding. And yet Linux is a highly successful, -well-regarded piece of software. - -\subsection{Pull-only versus shared-push collaboration} - -A perpetual source of heat in the open source community is whether a -development model in which people only ever pull changes from others -is ``better than'' one in which multiple people can push changes to a -shared repository. - -Typically, the backers of the shared-push model use tools that -actively enforce this approach. If you're using a centralised -revision control tool such as Subversion, there's no way to make a -choice over which model you'll use: the tool gives you shared-push, -and if you want to do anything else, you'll have to roll your own -approach on top (such as applying a patch by hand). - -A good distributed revision control tool, such as Mercurial, will -support both models. You and your collaborators can then structure -how you work together based on your own needs and preferences, not on -what contortions your tools force you into. - -\subsection{Where collaboration meets branch management} - -Once you and your team set up some shared repositories and start -propagating changes back and forth between local and shared repos, you -begin to face a related, but slightly different challenge: that of -managing the multiple directions in which your team may be moving at -once. Even though this subject is intimately related to how your team -collaborates, it's dense enough to merit treatment of its own, in -chapter~\ref{chap:branch}. - -\section{The technical side of sharing} - -The remainder of this chapter is devoted to the question of serving -data to your collaborators. - -\section{Informal sharing with \hgcmd{serve}} -\label{sec:collab:serve} - -Mercurial's \hgcmd{serve} command is wonderfully suited to small, -tight-knit, and fast-paced group environments. It also provides a -great way to get a feel for using Mercurial commands over a network. - -Run \hgcmd{serve} inside a repository, and in under a second it will -bring up a specialised HTTP server; this will accept connections from -any client, and serve up data for that repository until you terminate -it. Anyone who knows the URL of the server you just started, and can -talk to your computer over the network, can then use a web browser or -Mercurial to read data from that repository. A URL for a -\hgcmd{serve} instance running on a laptop is likely to look something -like \Verb|http://my-laptop.local:8000/|. - -The \hgcmd{serve} command is \emph{not} a general-purpose web server. -It can do only two things: -\begin{itemize} -\item Allow people to browse the history of the repository it's - serving, from their normal web browsers. -\item Speak Mercurial's wire protocol, so that people can - \hgcmd{clone} or \hgcmd{pull} changes from that repository. -\end{itemize} -In particular, \hgcmd{serve} won't allow remote users to \emph{modify} -your repository. It's intended for read-only use. - -If you're getting started with Mercurial, there's nothing to prevent -you from using \hgcmd{serve} to serve up a repository on your own -computer, then use commands like \hgcmd{clone}, \hgcmd{incoming}, and -so on to talk to that server as if the repository was hosted remotely. -This can help you to quickly get acquainted with using commands on -network-hosted repositories. - -\subsection{A few things to keep in mind} - -Because it provides unauthenticated read access to all clients, you -should only use \hgcmd{serve} in an environment where you either don't -care, or have complete control over, who can access your network and -pull data from your repository. - -The \hgcmd{serve} command knows nothing about any firewall software -you might have installed on your system or network. It cannot detect -or control your firewall software. If other people are unable to talk -to a running \hgcmd{serve} instance, the second thing you should do -(\emph{after} you make sure that they're using the correct URL) is -check your firewall configuration. - -By default, \hgcmd{serve} listens for incoming connections on -port~8000. If another process is already listening on the port you -want to use, you can specify a different port to listen on using the -\hgopt{serve}{-p} option. - -Normally, when \hgcmd{serve} starts, it prints no output, which can be -a bit unnerving. If you'd like to confirm that it is indeed running -correctly, and find out what URL you should send to your -collaborators, start it with the \hggopt{-v} option. - -\section{Using the Secure Shell (ssh) protocol} -\label{sec:collab:ssh} - -You can pull and push changes securely over a network connection using -the Secure Shell (\texttt{ssh}) protocol. To use this successfully, -you may have to do a little bit of configuration on the client or -server sides. - -If you're not familiar with ssh, it's a network protocol that lets you -securely communicate with another computer. To use it with Mercurial, -you'll be setting up one or more user accounts on a server so that -remote users can log in and execute commands. - -(If you \emph{are} familiar with ssh, you'll probably find some of the -material that follows to be elementary in nature.) - -\subsection{How to read and write ssh URLs} - -An ssh URL tends to look like this: -\begin{codesample2} - ssh://bos@hg.serpentine.com:22/hg/hgbook -\end{codesample2} -\begin{enumerate} -\item The ``\texttt{ssh://}'' part tells Mercurial to use the ssh - protocol. -\item The ``\texttt{bos@}'' component indicates what username to log - into the server as. You can leave this out if the remote username - is the same as your local username. -\item The ``\texttt{hg.serpentine.com}'' gives the hostname of the - server to log into. -\item The ``:22'' identifies the port number to connect to the server - on. The default port is~22, so you only need to specify this part - if you're \emph{not} using port~22. -\item The remainder of the URL is the local path to the repository on - the server. -\end{enumerate} - -There's plenty of scope for confusion with the path component of ssh -URLs, as there is no standard way for tools to interpret it. Some -programs behave differently than others when dealing with these paths. -This isn't an ideal situation, but it's unlikely to change. Please -read the following paragraphs carefully. - -Mercurial treats the path to a repository on the server as relative to -the remote user's home directory. For example, if user \texttt{foo} -on the server has a home directory of \dirname{/home/foo}, then an ssh -URL that contains a path component of \dirname{bar} -\emph{really} refers to the directory \dirname{/home/foo/bar}. - -If you want to specify a path relative to another user's home -directory, you can use a path that starts with a tilde character -followed by the user's name (let's call them \texttt{otheruser}), like -this. -\begin{codesample2} - ssh://server/~otheruser/hg/repo -\end{codesample2} - -And if you really want to specify an \emph{absolute} path on the -server, begin the path component with two slashes, as in this example. -\begin{codesample2} - ssh://server//absolute/path -\end{codesample2} - -\subsection{Finding an ssh client for your system} - -Almost every Unix-like system comes with OpenSSH preinstalled. If -you're using such a system, run \Verb|which ssh| to find out if -the \command{ssh} command is installed (it's usually in -\dirname{/usr/bin}). In the unlikely event that it isn't present, -take a look at your system documentation to figure out how to install -it. - -On Windows, you'll first need to download a suitable ssh -client. There are two alternatives. -\begin{itemize} -\item Simon Tatham's excellent PuTTY package~\cite{web:putty} provides - a complete suite of ssh client commands. -\item If you have a high tolerance for pain, you can use the Cygwin - port of OpenSSH. -\end{itemize} -In either case, you'll need to edit your \hgini\ file to tell -Mercurial where to find the actual client command. For example, if -you're using PuTTY, you'll need to use the \command{plink} command as -a command-line ssh client. -\begin{codesample2} - [ui] - ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key" -\end{codesample2} - -\begin{note} - The path to \command{plink} shouldn't contain any whitespace - characters, or Mercurial may not be able to run it correctly (so - putting it in \dirname{C:\\Program Files} is probably not a good - idea). -\end{note} - -\subsection{Generating a key pair} - -To avoid the need to repetitively type a password every time you need -to use your ssh client, I recommend generating a key pair. On a -Unix-like system, the \command{ssh-keygen} command will do the trick. -On Windows, if you're using PuTTY, the \command{puttygen} command is -what you'll need. - -When you generate a key pair, it's usually \emph{highly} advisable to -protect it with a passphrase. (The only time that you might not want -to do this is when you're using the ssh protocol for automated tasks -on a secure network.) - -Simply generating a key pair isn't enough, however. You'll need to -add the public key to the set of authorised keys for whatever user -you're logging in remotely as. For servers using OpenSSH (the vast -majority), this will mean adding the public key to a list in a file -called \sfilename{authorized\_keys} in their \sdirname{.ssh} -directory. - -On a Unix-like system, your public key will have a \filename{.pub} -extension. If you're using \command{puttygen} on Windows, you can -save the public key to a file of your choosing, or paste it from the -window it's displayed in straight into the -\sfilename{authorized\_keys} file. - -\subsection{Using an authentication agent} - -An authentication agent is a daemon that stores passphrases in memory -(so it will forget passphrases if you log out and log back in again). -An ssh client will notice if it's running, and query it for a -passphrase. If there's no authentication agent running, or the agent -doesn't store the necessary passphrase, you'll have to type your -passphrase every time Mercurial tries to communicate with a server on -your behalf (e.g.~whenever you pull or push changes). - -The downside of storing passphrases in an agent is that it's possible -for a well-prepared attacker to recover the plain text of your -passphrases, in some cases even if your system has been power-cycled. -You should make your own judgment as to whether this is an acceptable -risk. It certainly saves a lot of repeated typing. - -On Unix-like systems, the agent is called \command{ssh-agent}, and -it's often run automatically for you when you log in. You'll need to -use the \command{ssh-add} command to add passphrases to the agent's -store. On Windows, if you're using PuTTY, the \command{pageant} -command acts as the agent. It adds an icon to your system tray that -will let you manage stored passphrases. - -\subsection{Configuring the server side properly} - -Because ssh can be fiddly to set up if you're new to it, there's a -variety of things that can go wrong. Add Mercurial on top, and -there's plenty more scope for head-scratching. Most of these -potential problems occur on the server side, not the client side. The -good news is that once you've gotten a configuration working, it will -usually continue to work indefinitely. - -Before you try using Mercurial to talk to an ssh server, it's best to -make sure that you can use the normal \command{ssh} or \command{putty} -command to talk to the server first. If you run into problems with -using these commands directly, Mercurial surely won't work. Worse, it -will obscure the underlying problem. Any time you want to debug -ssh-related Mercurial problems, you should drop back to making sure -that plain ssh client commands work first, \emph{before} you worry -about whether there's a problem with Mercurial. - -The first thing to be sure of on the server side is that you can -actually log in from another machine at all. If you can't use -\command{ssh} or \command{putty} to log in, the error message you get -may give you a few hints as to what's wrong. The most common problems -are as follows. -\begin{itemize} -\item If you get a ``connection refused'' error, either there isn't an - SSH daemon running on the server at all, or it's inaccessible due to - firewall configuration. -\item If you get a ``no route to host'' error, you either have an - incorrect address for the server or a seriously locked down firewall - that won't admit its existence at all. -\item If you get a ``permission denied'' error, you may have mistyped - the username on the server, or you could have mistyped your key's - passphrase or the remote user's password. -\end{itemize} -In summary, if you're having trouble talking to the server's ssh -daemon, first make sure that one is running at all. On many systems -it will be installed, but disabled, by default. Once you're done with -this step, you should then check that the server's firewall is -configured to allow incoming connections on the port the ssh daemon is -listening on (usually~22). Don't worry about more exotic -possibilities for misconfiguration until you've checked these two -first. - -If you're using an authentication agent on the client side to store -passphrases for your keys, you ought to be able to log into the server -without being prompted for a passphrase or a password. If you're -prompted for a passphrase, there are a few possible culprits. -\begin{itemize} -\item You might have forgotten to use \command{ssh-add} or - \command{pageant} to store the passphrase. -\item You might have stored the passphrase for the wrong key. -\end{itemize} -If you're being prompted for the remote user's password, there are -another few possible problems to check. -\begin{itemize} -\item Either the user's home directory or their \sdirname{.ssh} - directory might have excessively liberal permissions. As a result, - the ssh daemon will not trust or read their - \sfilename{authorized\_keys} file. For example, a group-writable - home or \sdirname{.ssh} directory will often cause this symptom. -\item The user's \sfilename{authorized\_keys} file may have a problem. - If anyone other than the user owns or can write to that file, the - ssh daemon will not trust or read it. -\end{itemize} - -In the ideal world, you should be able to run the following command -successfully, and it should print exactly one line of output, the -current date and time. -\begin{codesample2} - ssh myserver date -\end{codesample2} - -If, on your server, you have login scripts that print banners or other -junk even when running non-interactive commands like this, you should -fix them before you continue, so that they only print output if -they're run interactively. Otherwise these banners will at least -clutter up Mercurial's output. Worse, they could potentially cause -problems with running Mercurial commands remotely. Mercurial makes -tries to detect and ignore banners in non-interactive \command{ssh} -sessions, but it is not foolproof. (If you're editing your login -scripts on your server, the usual way to see if a login script is -running in an interactive shell is to check the return code from the -command \Verb|tty -s|.) - -Once you've verified that plain old ssh is working with your server, -the next step is to ensure that Mercurial runs on the server. The -following command should run successfully: -\begin{codesample2} - ssh myserver hg version -\end{codesample2} -If you see an error message instead of normal \hgcmd{version} output, -this is usually because you haven't installed Mercurial to -\dirname{/usr/bin}. Don't worry if this is the case; you don't need -to do that. But you should check for a few possible problems. -\begin{itemize} -\item Is Mercurial really installed on the server at all? I know this - sounds trivial, but it's worth checking! -\item Maybe your shell's search path (usually set via the \envar{PATH} - environment variable) is simply misconfigured. -\item Perhaps your \envar{PATH} environment variable is only being set - to point to the location of the \command{hg} executable if the login - session is interactive. This can happen if you're setting the path - in the wrong shell login script. See your shell's documentation for - details. -\item The \envar{PYTHONPATH} environment variable may need to contain - the path to the Mercurial Python modules. It might not be set at - all; it could be incorrect; or it may be set only if the login is - interactive. -\end{itemize} - -If you can run \hgcmd{version} over an ssh connection, well done! -You've got the server and client sorted out. You should now be able -to use Mercurial to access repositories hosted by that username on -that server. If you run into problems with Mercurial and ssh at this -point, try using the \hggopt{--debug} option to get a clearer picture -of what's going on. - -\subsection{Using compression with ssh} - -Mercurial does not compress data when it uses the ssh protocol, -because the ssh protocol can transparently compress data. However, -the default behaviour of ssh clients is \emph{not} to request -compression. - -Over any network other than a fast LAN (even a wireless network), -using compression is likely to significantly speed up Mercurial's -network operations. For example, over a WAN, someone measured -compression as reducing the amount of time required to clone a -particularly large repository from~51 minutes to~17 minutes. - -Both \command{ssh} and \command{plink} accept a \cmdopt{ssh}{-C} -option which turns on compression. You can easily edit your \hgrc\ to -enable compression for all of Mercurial's uses of the ssh protocol. -\begin{codesample2} - [ui] - ssh = ssh -C -\end{codesample2} - -If you use \command{ssh}, you can configure it to always use -compression when talking to your server. To do this, edit your -\sfilename{.ssh/config} file (which may not yet exist), as follows. -\begin{codesample2} - Host hg - Compression yes - HostName hg.example.com -\end{codesample2} -This defines an alias, \texttt{hg}. When you use it on the -\command{ssh} command line or in a Mercurial \texttt{ssh}-protocol -URL, it will cause \command{ssh} to connect to \texttt{hg.example.com} -and use compression. This gives you both a shorter name to type and -compression, each of which is a good thing in its own right. - -\section{Serving over HTTP using CGI} -\label{sec:collab:cgi} - -Depending on how ambitious you are, configuring Mercurial's CGI -interface can take anything from a few moments to several hours. - -We'll begin with the simplest of examples, and work our way towards a -more complex configuration. Even for the most basic case, you're -almost certainly going to need to read and modify your web server's -configuration. - -\begin{note} - Configuring a web server is a complex, fiddly, and highly - system-dependent activity. I can't possibly give you instructions - that will cover anything like all of the cases you will encounter. - Please use your discretion and judgment in following the sections - below. Be prepared to make plenty of mistakes, and to spend a lot - of time reading your server's error logs. -\end{note} - -\subsection{Web server configuration checklist} - -Before you continue, do take a few moments to check a few aspects of -your system's setup. - -\begin{enumerate} -\item Do you have a web server installed at all? Mac OS X ships with - Apache, but many other systems may not have a web server installed. -\item If you have a web server installed, is it actually running? On - most systems, even if one is present, it will be disabled by - default. -\item Is your server configured to allow you to run CGI programs in - the directory where you plan to do so? Most servers default to - explicitly disabling the ability to run CGI programs. -\end{enumerate} - -If you don't have a web server installed, and don't have substantial -experience configuring Apache, you should consider using the -\texttt{lighttpd} web server instead of Apache. Apache has a -well-deserved reputation for baroque and confusing configuration. -While \texttt{lighttpd} is less capable in some ways than Apache, most -of these capabilities are not relevant to serving Mercurial -repositories. And \texttt{lighttpd} is undeniably \emph{much} easier -to get started with than Apache. - -\subsection{Basic CGI configuration} - -On Unix-like systems, it's common for users to have a subdirectory -named something like \dirname{public\_html} in their home directory, -from which they can serve up web pages. A file named \filename{foo} -in this directory will be accessible at a URL of the form -\texttt{http://www.example.com/\~{}username/foo}. - -To get started, find the \sfilename{hgweb.cgi} script that should be -present in your Mercurial installation. If you can't quickly find a -local copy on your system, simply download one from the master -Mercurial repository at -\url{http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi}. - -You'll need to copy this script into your \dirname{public\_html} -directory, and ensure that it's executable. -\begin{codesample2} - cp .../hgweb.cgi ~/public_html - chmod 755 ~/public_html/hgweb.cgi -\end{codesample2} -The \texttt{755} argument to \command{chmod} is a little more general -than just making the script executable: it ensures that the script is -executable by anyone, and that ``group'' and ``other'' write -permissions are \emph{not} set. If you were to leave those write -permissions enabled, Apache's \texttt{suexec} subsystem would likely -refuse to execute the script. In fact, \texttt{suexec} also insists -that the \emph{directory} in which the script resides must not be -writable by others. -\begin{codesample2} - chmod 755 ~/public_html -\end{codesample2} - -\subsubsection{What could \emph{possibly} go wrong?} -\label{sec:collab:wtf} - -Once you've copied the CGI script into place, go into a web browser, -and try to open the URL \url{http://myhostname/~myuser/hgweb.cgi}, -\emph{but} brace yourself for instant failure. There's a high -probability that trying to visit this URL will fail, and there are -many possible reasons for this. In fact, you're likely to stumble -over almost every one of the possible errors below, so please read -carefully. The following are all of the problems I ran into on a -system running Fedora~7, with a fresh installation of Apache, and a -user account that I created specially to perform this exercise. - -Your web server may have per-user directories disabled. If you're -using Apache, search your config file for a \texttt{UserDir} -directive. If there's none present, per-user directories will be -disabled. If one exists, but its value is \texttt{disabled}, then -per-user directories will be disabled. Otherwise, the string after -\texttt{UserDir} gives the name of the subdirectory that Apache will -look in under your home directory, for example \dirname{public\_html}. - -Your file access permissions may be too restrictive. The web server -must be able to traverse your home directory and directories under -your \dirname{public\_html} directory, and read files under the latter -too. Here's a quick recipe to help you to make your permissions more -appropriate. -\begin{codesample2} - chmod 755 ~ - find ~/public_html -type d -print0 | xargs -0r chmod 755 - find ~/public_html -type f -print0 | xargs -0r chmod 644 -\end{codesample2} - -The other possibility with permissions is that you might get a -completely empty window when you try to load the script. In this -case, it's likely that your access permissions are \emph{too - permissive}. Apache's \texttt{suexec} subsystem won't execute a -script that's group-~or world-writable, for example. - -Your web server may be configured to disallow execution of CGI -programs in your per-user web directory. Here's Apache's -default per-user configuration from my Fedora system. -\begin{codesample2} - - AllowOverride FileInfo AuthConfig Limit - Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec - - Order allow,deny - Allow from all - - - Order deny,allow - Deny from all - - -\end{codesample2} -If you find a similar-looking \texttt{Directory} group in your Apache -configuration, the directive to look at inside it is \texttt{Options}. -Add \texttt{ExecCGI} to the end of this list if it's missing, and -restart the web server. - -If you find that Apache serves you the text of the CGI script instead -of executing it, you may need to either uncomment (if already present) -or add a directive like this. -\begin{codesample2} - AddHandler cgi-script .cgi -\end{codesample2} - -The next possibility is that you might be served with a colourful -Python backtrace claiming that it can't import a -\texttt{mercurial}-related module. This is actually progress! The -server is now capable of executing your CGI script. This error is -only likely to occur if you're running a private installation of -Mercurial, instead of a system-wide version. Remember that the web -server runs the CGI program without any of the environment variables -that you take for granted in an interactive session. If this error -happens to you, edit your copy of \sfilename{hgweb.cgi} and follow the -directions inside it to correctly set your \envar{PYTHONPATH} -environment variable. - -Finally, you are \emph{certain} to by served with another colourful -Python backtrace: this one will complain that it can't find -\dirname{/path/to/repository}. Edit your \sfilename{hgweb.cgi} script -and replace the \dirname{/path/to/repository} string with the complete -path to the repository you want to serve up. - -At this point, when you try to reload the page, you should be -presented with a nice HTML view of your repository's history. Whew! - -\subsubsection{Configuring lighttpd} - -To be exhaustive in my experiments, I tried configuring the -increasingly popular \texttt{lighttpd} web server to serve the same -repository as I described with Apache above. I had already overcome -all of the problems I outlined with Apache, many of which are not -server-specific. As a result, I was fairly sure that my file and -directory permissions were good, and that my \sfilename{hgweb.cgi} -script was properly edited. - -Once I had Apache running, getting \texttt{lighttpd} to serve the -repository was a snap (in other words, even if you're trying to use -\texttt{lighttpd}, you should read the Apache section). I first had -to edit the \texttt{mod\_access} section of its config file to enable -\texttt{mod\_cgi} and \texttt{mod\_userdir}, both of which were -disabled by default on my system. I then added a few lines to the end -of the config file, to configure these modules. -\begin{codesample2} - userdir.path = "public_html" - cgi.assign = ( ".cgi" => "" ) -\end{codesample2} -With this done, \texttt{lighttpd} ran immediately for me. If I had -configured \texttt{lighttpd} before Apache, I'd almost certainly have -run into many of the same system-level configuration problems as I did -with Apache. However, I found \texttt{lighttpd} to be noticeably -easier to configure than Apache, even though I've used Apache for over -a decade, and this was my first exposure to \texttt{lighttpd}. - -\subsection{Sharing multiple repositories with one CGI script} - -The \sfilename{hgweb.cgi} script only lets you publish a single -repository, which is an annoying restriction. If you want to publish -more than one without wracking yourself with multiple copies of the -same script, each with different names, a better choice is to use the -\sfilename{hgwebdir.cgi} script. - -The procedure to configure \sfilename{hgwebdir.cgi} is only a little -more involved than for \sfilename{hgweb.cgi}. First, you must obtain -a copy of the script. If you don't have one handy, you can download a -copy from the master Mercurial repository at -\url{http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi}. - -You'll need to copy this script into your \dirname{public\_html} -directory, and ensure that it's executable. -\begin{codesample2} - cp .../hgwebdir.cgi ~/public_html - chmod 755 ~/public_html ~/public_html/hgwebdir.cgi -\end{codesample2} -With basic configuration out of the way, try to visit -\url{http://myhostname/~myuser/hgwebdir.cgi} in your browser. It -should display an empty list of repositories. If you get a blank -window or error message, try walking through the list of potential -problems in section~\ref{sec:collab:wtf}. - -The \sfilename{hgwebdir.cgi} script relies on an external -configuration file. By default, it searches for a file named -\sfilename{hgweb.config} in the same directory as itself. You'll need -to create this file, and make it world-readable. The format of the -file is similar to a Windows ``ini'' file, as understood by Python's -\texttt{ConfigParser}~\cite{web:configparser} module. - -The easiest way to configure \sfilename{hgwebdir.cgi} is with a -section named \texttt{collections}. This will automatically publish -\emph{every} repository under the directories you name. The section -should look like this: -\begin{codesample2} - [collections] - /my/root = /my/root -\end{codesample2} -Mercurial interprets this by looking at the directory name on the -\emph{right} hand side of the ``\texttt{=}'' sign; finding -repositories in that directory hierarchy; and using the text on the -\emph{left} to strip off matching text from the names it will actually -list in the web interface. The remaining component of a path after -this stripping has occurred is called a ``virtual path''. - -Given the example above, if we have a repository whose local path is -\dirname{/my/root/this/repo}, the CGI script will strip the leading -\dirname{/my/root} from the name, and publish the repository with a -virtual path of \dirname{this/repo}. If the base URL for our CGI -script is \url{http://myhostname/~myuser/hgwebdir.cgi}, the complete -URL for that repository will be -\url{http://myhostname/~myuser/hgwebdir.cgi/this/repo}. - -If we replace \dirname{/my/root} on the left hand side of this example -with \dirname{/my}, then \sfilename{hgwebdir.cgi} will only strip off -\dirname{/my} from the repository name, and will give us a virtual -path of \dirname{root/this/repo} instead of \dirname{this/repo}. - -The \sfilename{hgwebdir.cgi} script will recursively search each -directory listed in the \texttt{collections} section of its -configuration file, but it will \texttt{not} recurse into the -repositories it finds. - -The \texttt{collections} mechanism makes it easy to publish many -repositories in a ``fire and forget'' manner. You only need to set up -the CGI script and configuration file one time. Afterwards, you can -publish or unpublish a repository at any time by simply moving it -into, or out of, the directory hierarchy in which you've configured -\sfilename{hgwebdir.cgi} to look. - -\subsubsection{Explicitly specifying which repositories to publish} - -In addition to the \texttt{collections} mechanism, the -\sfilename{hgwebdir.cgi} script allows you to publish a specific list -of repositories. To do so, create a \texttt{paths} section, with -contents of the following form. -\begin{codesample2} - [paths] - repo1 = /my/path/to/some/repo - repo2 = /some/path/to/another -\end{codesample2} -In this case, the virtual path (the component that will appear in a -URL) is on the left hand side of each definition, while the path to -the repository is on the right. Notice that there does not need to be -any relationship between the virtual path you choose and the location -of a repository in your filesystem. - -If you wish, you can use both the \texttt{collections} and -\texttt{paths} mechanisms simultaneously in a single configuration -file. - -\begin{note} - If multiple repositories have the same virtual path, - \sfilename{hgwebdir.cgi} will not report an error. Instead, it will - behave unpredictably. -\end{note} - -\subsection{Downloading source archives} - -Mercurial's web interface lets users download an archive of any -revision. This archive will contain a snapshot of the working -directory as of that revision, but it will not contain a copy of the -repository data. - -By default, this feature is not enabled. To enable it, you'll need to -add an \rcitem{web}{allow\_archive} item to the \rcsection{web} -section of your \hgrc. - -\subsection{Web configuration options} - -Mercurial's web interfaces (the \hgcmd{serve} command, and the -\sfilename{hgweb.cgi} and \sfilename{hgwebdir.cgi} scripts) have a -number of configuration options that you can set. These belong in a -section named \rcsection{web}. -\begin{itemize} -\item[\rcitem{web}{allow\_archive}] Determines which (if any) archive - download mechanisms Mercurial supports. If you enable this - feature, users of the web interface will be able to download an - archive of whatever revision of a repository they are viewing. - To enable the archive feature, this item must take the form of a - sequence of words drawn from the list below. - \begin{itemize} - \item[\texttt{bz2}] A \command{tar} archive, compressed using - \texttt{bzip2} compression. This has the best compression ratio, - but uses the most CPU time on the server. - \item[\texttt{gz}] A \command{tar} archive, compressed using - \texttt{gzip} compression. - \item[\texttt{zip}] A \command{zip} archive, compressed using LZW - compression. This format has the worst compression ratio, but is - widely used in the Windows world. - \end{itemize} - If you provide an empty list, or don't have an - \rcitem{web}{allow\_archive} entry at all, this feature will be - disabled. Here is an example of how to enable all three supported - formats. - \begin{codesample4} - [web] - allow_archive = bz2 gz zip - \end{codesample4} -\item[\rcitem{web}{allowpull}] Boolean. Determines whether the web - interface allows remote users to \hgcmd{pull} and \hgcmd{clone} this - repository over~HTTP. If set to \texttt{no} or \texttt{false}, only - the ``human-oriented'' portion of the web interface is available. -\item[\rcitem{web}{contact}] String. A free-form (but preferably - brief) string identifying the person or group in charge of the - repository. This often contains the name and email address of a - person or mailing list. It often makes sense to place this entry in - a repository's own \sfilename{.hg/hgrc} file, but it can make sense - to use in a global \hgrc\ if every repository has a single - maintainer. -\item[\rcitem{web}{maxchanges}] Integer. The default maximum number - of changesets to display in a single page of output. -\item[\rcitem{web}{maxfiles}] Integer. The default maximum number - of modified files to display in a single page of output. -\item[\rcitem{web}{stripes}] Integer. If the web interface displays - alternating ``stripes'' to make it easier to visually align rows - when you are looking at a table, this number controls the number of - rows in each stripe. -\item[\rcitem{web}{style}] Controls the template Mercurial uses to - display the web interface. Mercurial ships with two web templates, - named \texttt{default} and \texttt{gitweb} (the latter is much more - visually attractive). You can also specify a custom template of - your own; see chapter~\ref{chap:template} for details. Here, you - can see how to enable the \texttt{gitweb} style. - \begin{codesample4} - [web] - style = gitweb - \end{codesample4} -\item[\rcitem{web}{templates}] Path. The directory in which to search - for template files. By default, Mercurial searches in the directory - in which it was installed. -\end{itemize} -If you are using \sfilename{hgwebdir.cgi}, you can place a few -configuration items in a \rcsection{web} section of the -\sfilename{hgweb.config} file instead of a \hgrc\ file, for -convenience. These items are \rcitem{web}{motd} and -\rcitem{web}{style}. - -\subsubsection{Options specific to an individual repository} - -A few \rcsection{web} configuration items ought to be placed in a -repository's local \sfilename{.hg/hgrc}, rather than a user's or -global \hgrc. -\begin{itemize} -\item[\rcitem{web}{description}] String. A free-form (but preferably - brief) string that describes the contents or purpose of the - repository. -\item[\rcitem{web}{name}] String. The name to use for the repository - in the web interface. This overrides the default name, which is the - last component of the repository's path. -\end{itemize} - -\subsubsection{Options specific to the \hgcmd{serve} command} - -Some of the items in the \rcsection{web} section of a \hgrc\ file are -only for use with the \hgcmd{serve} command. -\begin{itemize} -\item[\rcitem{web}{accesslog}] Path. The name of a file into which to - write an access log. By default, the \hgcmd{serve} command writes - this information to standard output, not to a file. Log entries are - written in the standard ``combined'' file format used by almost all - web servers. -\item[\rcitem{web}{address}] String. The local address on which the - server should listen for incoming connections. By default, the - server listens on all addresses. -\item[\rcitem{web}{errorlog}] Path. The name of a file into which to - write an error log. By default, the \hgcmd{serve} command writes this - information to standard error, not to a file. -\item[\rcitem{web}{ipv6}] Boolean. Whether to use the IPv6 protocol. - By default, IPv6 is not used. -\item[\rcitem{web}{port}] Integer. The TCP~port number on which the - server should listen. The default port number used is~8000. -\end{itemize} - -\subsubsection{Choosing the right \hgrc\ file to add \rcsection{web} - items to} - -It is important to remember that a web server like Apache or -\texttt{lighttpd} will run under a user~ID that is different to yours. -CGI scripts run by your server, such as \sfilename{hgweb.cgi}, will -usually also run under that user~ID. - -If you add \rcsection{web} items to your own personal \hgrc\ file, CGI -scripts won't read that \hgrc\ file. Those settings will thus only -affect the behaviour of the \hgcmd{serve} command when you run it. To -cause CGI scripts to see your settings, either create a \hgrc\ file in -the home directory of the user ID that runs your web server, or add -those settings to a system-wide \hgrc\ file. - - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/concepts.tex --- a/fr/concepts.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,577 +0,0 @@ -\chapter{Behind the scenes} -\label{chap:concepts} - -Unlike many revision control systems, the concepts upon which -Mercurial is built are simple enough that it's easy to understand how -the software really works. Knowing this certainly isn't necessary, -but I find it useful to have a ``mental model'' of what's going on. - -This understanding gives me confidence that Mercurial has been -carefully designed to be both \emph{safe} and \emph{efficient}. And -just as importantly, if it's easy for me to retain a good idea of what -the software is doing when I perform a revision control task, I'm less -likely to be surprised by its behaviour. - -In this chapter, we'll initially cover the core concepts behind -Mercurial's design, then continue to discuss some of the interesting -details of its implementation. - -\section{Mercurial's historical record} - -\subsection{Tracking the history of a single file} - -When Mercurial tracks modifications to a file, it stores the history -of that file in a metadata object called a \emph{filelog}. Each entry -in the filelog contains enough information to reconstruct one revision -of the file that is being tracked. Filelogs are stored as files in -the \sdirname{.hg/store/data} directory. A filelog contains two kinds -of information: revision data, and an index to help Mercurial to find -a revision efficiently. - -A file that is large, or has a lot of history, has its filelog stored -in separate data (``\texttt{.d}'' suffix) and index (``\texttt{.i}'' -suffix) files. For small files without much history, the revision -data and index are combined in a single ``\texttt{.i}'' file. The -correspondence between a file in the working directory and the filelog -that tracks its history in the repository is illustrated in -figure~\ref{fig:concepts:filelog}. - -\begin{figure}[ht] - \centering - \grafix{filelog} - \caption{Relationships between files in working directory and - filelogs in repository} - \label{fig:concepts:filelog} -\end{figure} - -\subsection{Managing tracked files} - -Mercurial uses a structure called a \emph{manifest} to collect -together information about the files that it tracks. Each entry in -the manifest contains information about the files present in a single -changeset. An entry records which files are present in the changeset, -the revision of each file, and a few other pieces of file metadata. - -\subsection{Recording changeset information} - -The \emph{changelog} contains information about each changeset. Each -revision records who committed a change, the changeset comment, other -pieces of changeset-related information, and the revision of the -manifest to use. - -\subsection{Relationships between revisions} - -Within a changelog, a manifest, or a filelog, each revision stores a -pointer to its immediate parent (or to its two parents, if it's a -merge revision). As I mentioned above, there are also relationships -between revisions \emph{across} these structures, and they are -hierarchical in nature. - -For every changeset in a repository, there is exactly one revision -stored in the changelog. Each revision of the changelog contains a -pointer to a single revision of the manifest. A revision of the -manifest stores a pointer to a single revision of each filelog tracked -when that changeset was created. These relationships are illustrated -in figure~\ref{fig:concepts:metadata}. - -\begin{figure}[ht] - \centering - \grafix{metadata} - \caption{Metadata relationships} - \label{fig:concepts:metadata} -\end{figure} - -As the illustration shows, there is \emph{not} a ``one to one'' -relationship between revisions in the changelog, manifest, or filelog. -If the manifest hasn't changed between two changesets, the changelog -entries for those changesets will point to the same revision of the -manifest. If a file that Mercurial tracks hasn't changed between two -changesets, the entry for that file in the two revisions of the -manifest will point to the same revision of its filelog. - -\section{Safe, efficient storage} - -The underpinnings of changelogs, manifests, and filelogs are provided -by a single structure called the \emph{revlog}. - -\subsection{Efficient storage} - -The revlog provides efficient storage of revisions using a -\emph{delta} mechanism. Instead of storing a complete copy of a file -for each revision, it stores the changes needed to transform an older -revision into the new revision. For many kinds of file data, these -deltas are typically a fraction of a percent of the size of a full -copy of a file. - -Some obsolete revision control systems can only work with deltas of -text files. They must either store binary files as complete snapshots -or encoded into a text representation, both of which are wasteful -approaches. Mercurial can efficiently handle deltas of files with -arbitrary binary contents; it doesn't need to treat text as special. - -\subsection{Safe operation} -\label{sec:concepts:txn} - -Mercurial only ever \emph{appends} data to the end of a revlog file. -It never modifies a section of a file after it has written it. This -is both more robust and efficient than schemes that need to modify or -rewrite data. - -In addition, Mercurial treats every write as part of a -\emph{transaction} that can span a number of files. A transaction is -\emph{atomic}: either the entire transaction succeeds and its effects -are all visible to readers in one go, or the whole thing is undone. -This guarantee of atomicity means that if you're running two copies of -Mercurial, where one is reading data and one is writing it, the reader -will never see a partially written result that might confuse it. - -The fact that Mercurial only appends to files makes it easier to -provide this transactional guarantee. The easier it is to do stuff -like this, the more confident you should be that it's done correctly. - -\subsection{Fast retrieval} - -Mercurial cleverly avoids a pitfall common to all earlier -revision control systems: the problem of \emph{inefficient retrieval}. -Most revision control systems store the contents of a revision as an -incremental series of modifications against a ``snapshot''. To -reconstruct a specific revision, you must first read the snapshot, and -then every one of the revisions between the snapshot and your target -revision. The more history that a file accumulates, the more -revisions you must read, hence the longer it takes to reconstruct a -particular revision. - -\begin{figure}[ht] - \centering - \grafix{snapshot} - \caption{Snapshot of a revlog, with incremental deltas} - \label{fig:concepts:snapshot} -\end{figure} - -The innovation that Mercurial applies to this problem is simple but -effective. Once the cumulative amount of delta information stored -since the last snapshot exceeds a fixed threshold, it stores a new -snapshot (compressed, of course), instead of another delta. This -makes it possible to reconstruct \emph{any} revision of a file -quickly. This approach works so well that it has since been copied by -several other revision control systems. - -Figure~\ref{fig:concepts:snapshot} illustrates the idea. In an entry -in a revlog's index file, Mercurial stores the range of entries from -the data file that it must read to reconstruct a particular revision. - -\subsubsection{Aside: the influence of video compression} - -If you're familiar with video compression or have ever watched a TV -feed through a digital cable or satellite service, you may know that -most video compression schemes store each frame of video as a delta -against its predecessor frame. In addition, these schemes use -``lossy'' compression techniques to increase the compression ratio, so -visual errors accumulate over the course of a number of inter-frame -deltas. - -Because it's possible for a video stream to ``drop out'' occasionally -due to signal glitches, and to limit the accumulation of artefacts -introduced by the lossy compression process, video encoders -periodically insert a complete frame (called a ``key frame'') into the -video stream; the next delta is generated against that frame. This -means that if the video signal gets interrupted, it will resume once -the next key frame is received. Also, the accumulation of encoding -errors restarts anew with each key frame. - -\subsection{Identification and strong integrity} - -Along with delta or snapshot information, a revlog entry contains a -cryptographic hash of the data that it represents. This makes it -difficult to forge the contents of a revision, and easy to detect -accidental corruption. - -Hashes provide more than a mere check against corruption; they are -used as the identifiers for revisions. The changeset identification -hashes that you see as an end user are from revisions of the -changelog. Although filelogs and the manifest also use hashes, -Mercurial only uses these behind the scenes. - -Mercurial verifies that hashes are correct when it retrieves file -revisions and when it pulls changes from another repository. If it -encounters an integrity problem, it will complain and stop whatever -it's doing. - -In addition to the effect it has on retrieval efficiency, Mercurial's -use of periodic snapshots makes it more robust against partial data -corruption. If a revlog becomes partly corrupted due to a hardware -error or system bug, it's often possible to reconstruct some or most -revisions from the uncorrupted sections of the revlog, both before and -after the corrupted section. This would not be possible with a -delta-only storage model. - -\section{Revision history, branching, - and merging} - -Every entry in a Mercurial revlog knows the identity of its immediate -ancestor revision, usually referred to as its \emph{parent}. In fact, -a revision contains room for not one parent, but two. Mercurial uses -a special hash, called the ``null ID'', to represent the idea ``there -is no parent here''. This hash is simply a string of zeroes. - -In figure~\ref{fig:concepts:revlog}, you can see an example of the -conceptual structure of a revlog. Filelogs, manifests, and changelogs -all have this same structure; they differ only in the kind of data -stored in each delta or snapshot. - -The first revision in a revlog (at the bottom of the image) has the -null ID in both of its parent slots. For a ``normal'' revision, its -first parent slot contains the ID of its parent revision, and its -second contains the null ID, indicating that the revision has only one -real parent. Any two revisions that have the same parent ID are -branches. A revision that represents a merge between branches has two -normal revision IDs in its parent slots. - -\begin{figure}[ht] - \centering - \grafix{revlog} - \caption{} - \label{fig:concepts:revlog} -\end{figure} - -\section{The working directory} - -In the working directory, Mercurial stores a snapshot of the files -from the repository as of a particular changeset. - -The working directory ``knows'' which changeset it contains. When you -update the working directory to contain a particular changeset, -Mercurial looks up the appropriate revision of the manifest to find -out which files it was tracking at the time that changeset was -committed, and which revision of each file was then current. It then -recreates a copy of each of those files, with the same contents it had -when the changeset was committed. - -The \emph{dirstate} contains Mercurial's knowledge of the working -directory. This details which changeset the working directory is -updated to, and all of the files that Mercurial is tracking in the -working directory. - -Just as a revision of a revlog has room for two parents, so that it -can represent either a normal revision (with one parent) or a merge of -two earlier revisions, the dirstate has slots for two parents. When -you use the \hgcmd{update} command, the changeset that you update to -is stored in the ``first parent'' slot, and the null ID in the second. -When you \hgcmd{merge} with another changeset, the first parent -remains unchanged, and the second parent is filled in with the -changeset you're merging with. The \hgcmd{parents} command tells you -what the parents of the dirstate are. - -\subsection{What happens when you commit} - -The dirstate stores parent information for more than just book-keeping -purposes. Mercurial uses the parents of the dirstate as \emph{the - parents of a new changeset} when you perform a commit. - -\begin{figure}[ht] - \centering - \grafix{wdir} - \caption{The working directory can have two parents} - \label{fig:concepts:wdir} -\end{figure} - -Figure~\ref{fig:concepts:wdir} shows the normal state of the working -directory, where it has a single changeset as parent. That changeset -is the \emph{tip}, the newest changeset in the repository that has no -children. - -\begin{figure}[ht] - \centering - \grafix{wdir-after-commit} - \caption{The working directory gains new parents after a commit} - \label{fig:concepts:wdir-after-commit} -\end{figure} - -It's useful to think of the working directory as ``the changeset I'm -about to commit''. Any files that you tell Mercurial that you've -added, removed, renamed, or copied will be reflected in that -changeset, as will modifications to any files that Mercurial is -already tracking; the new changeset will have the parents of the -working directory as its parents. - -After a commit, Mercurial will update the parents of the working -directory, so that the first parent is the ID of the new changeset, -and the second is the null ID. This is shown in -figure~\ref{fig:concepts:wdir-after-commit}. Mercurial doesn't touch -any of the files in the working directory when you commit; it just -modifies the dirstate to note its new parents. - -\subsection{Creating a new head} - -It's perfectly normal to update the working directory to a changeset -other than the current tip. For example, you might want to know what -your project looked like last Tuesday, or you could be looking through -changesets to see which one introduced a bug. In cases like this, the -natural thing to do is update the working directory to the changeset -you're interested in, and then examine the files in the working -directory directly to see their contents as they were when you -committed that changeset. The effect of this is shown in -figure~\ref{fig:concepts:wdir-pre-branch}. - -\begin{figure}[ht] - \centering - \grafix{wdir-pre-branch} - \caption{The working directory, updated to an older changeset} - \label{fig:concepts:wdir-pre-branch} -\end{figure} - -Having updated the working directory to an older changeset, what -happens if you make some changes, and then commit? Mercurial behaves -in the same way as I outlined above. The parents of the working -directory become the parents of the new changeset. This new changeset -has no children, so it becomes the new tip. And the repository now -contains two changesets that have no children; we call these -\emph{heads}. You can see the structure that this creates in -figure~\ref{fig:concepts:wdir-branch}. - -\begin{figure}[ht] - \centering - \grafix{wdir-branch} - \caption{After a commit made while synced to an older changeset} - \label{fig:concepts:wdir-branch} -\end{figure} - -\begin{note} - If you're new to Mercurial, you should keep in mind a common - ``error'', which is to use the \hgcmd{pull} command without any - options. By default, the \hgcmd{pull} command \emph{does not} - update the working directory, so you'll bring new changesets into - your repository, but the working directory will stay synced at the - same changeset as before the pull. If you make some changes and - commit afterwards, you'll thus create a new head, because your - working directory isn't synced to whatever the current tip is. - - I put the word ``error'' in quotes because all that you need to do - to rectify this situation is \hgcmd{merge}, then \hgcmd{commit}. In - other words, this almost never has negative consequences; it just - surprises people. I'll discuss other ways to avoid this behaviour, - and why Mercurial behaves in this initially surprising way, later - on. -\end{note} - -\subsection{Merging heads} - -When you run the \hgcmd{merge} command, Mercurial leaves the first -parent of the working directory unchanged, and sets the second parent -to the changeset you're merging with, as shown in -figure~\ref{fig:concepts:wdir-merge}. - -\begin{figure}[ht] - \centering - \grafix{wdir-merge} - \caption{Merging two heads} - \label{fig:concepts:wdir-merge} -\end{figure} - -Mercurial also has to modify the working directory, to merge the files -managed in the two changesets. Simplified a little, the merging -process goes like this, for every file in the manifests of both -changesets. -\begin{itemize} -\item If neither changeset has modified a file, do nothing with that - file. -\item If one changeset has modified a file, and the other hasn't, - create the modified copy of the file in the working directory. -\item If one changeset has removed a file, and the other hasn't (or - has also deleted it), delete the file from the working directory. -\item If one changeset has removed a file, but the other has modified - the file, ask the user what to do: keep the modified file, or remove - it? -\item If both changesets have modified a file, invoke an external - merge program to choose the new contents for the merged file. This - may require input from the user. -\item If one changeset has modified a file, and the other has renamed - or copied the file, make sure that the changes follow the new name - of the file. -\end{itemize} -There are more details---merging has plenty of corner cases---but -these are the most common choices that are involved in a merge. As -you can see, most cases are completely automatic, and indeed most -merges finish automatically, without requiring your input to resolve -any conflicts. - -When you're thinking about what happens when you commit after a merge, -once again the working directory is ``the changeset I'm about to -commit''. After the \hgcmd{merge} command completes, the working -directory has two parents; these will become the parents of the new -changeset. - -Mercurial lets you perform multiple merges, but you must commit the -results of each individual merge as you go. This is necessary because -Mercurial only tracks two parents for both revisions and the working -directory. While it would be technically possible to merge multiple -changesets at once, the prospect of user confusion and making a -terrible mess of a merge immediately becomes overwhelming. - -\section{Other interesting design features} - -In the sections above, I've tried to highlight some of the most -important aspects of Mercurial's design, to illustrate that it pays -careful attention to reliability and performance. However, the -attention to detail doesn't stop there. There are a number of other -aspects of Mercurial's construction that I personally find -interesting. I'll detail a few of them here, separate from the ``big -ticket'' items above, so that if you're interested, you can gain a -better idea of the amount of thinking that goes into a well-designed -system. - -\subsection{Clever compression} - -When appropriate, Mercurial will store both snapshots and deltas in -compressed form. It does this by always \emph{trying to} compress a -snapshot or delta, but only storing the compressed version if it's -smaller than the uncompressed version. - -This means that Mercurial does ``the right thing'' when storing a file -whose native form is compressed, such as a \texttt{zip} archive or a -JPEG image. When these types of files are compressed a second time, -the resulting file is usually bigger than the once-compressed form, -and so Mercurial will store the plain \texttt{zip} or JPEG. - -Deltas between revisions of a compressed file are usually larger than -snapshots of the file, and Mercurial again does ``the right thing'' in -these cases. It finds that such a delta exceeds the threshold at -which it should store a complete snapshot of the file, so it stores -the snapshot, again saving space compared to a naive delta-only -approach. - -\subsubsection{Network recompression} - -When storing revisions on disk, Mercurial uses the ``deflate'' -compression algorithm (the same one used by the popular \texttt{zip} -archive format), which balances good speed with a respectable -compression ratio. However, when transmitting revision data over a -network connection, Mercurial uncompresses the compressed revision -data. - -If the connection is over HTTP, Mercurial recompresses the entire -stream of data using a compression algorithm that gives a better -compression ratio (the Burrows-Wheeler algorithm from the widely used -\texttt{bzip2} compression package). This combination of algorithm -and compression of the entire stream (instead of a revision at a time) -substantially reduces the number of bytes to be transferred, yielding -better network performance over almost all kinds of network. - -(If the connection is over \command{ssh}, Mercurial \emph{doesn't} -recompress the stream, because \command{ssh} can already do this -itself.) - -\subsection{Read/write ordering and atomicity} - -Appending to files isn't the whole story when it comes to guaranteeing -that a reader won't see a partial write. If you recall -figure~\ref{fig:concepts:metadata}, revisions in the changelog point to -revisions in the manifest, and revisions in the manifest point to -revisions in filelogs. This hierarchy is deliberate. - -A writer starts a transaction by writing filelog and manifest data, -and doesn't write any changelog data until those are finished. A -reader starts by reading changelog data, then manifest data, followed -by filelog data. - -Since the writer has always finished writing filelog and manifest data -before it writes to the changelog, a reader will never read a pointer -to a partially written manifest revision from the changelog, and it will -never read a pointer to a partially written filelog revision from the -manifest. - -\subsection{Concurrent access} - -The read/write ordering and atomicity guarantees mean that Mercurial -never needs to \emph{lock} a repository when it's reading data, even -if the repository is being written to while the read is occurring. -This has a big effect on scalability; you can have an arbitrary number -of Mercurial processes safely reading data from a repository safely -all at once, no matter whether it's being written to or not. - -The lockless nature of reading means that if you're sharing a -repository on a multi-user system, you don't need to grant other local -users permission to \emph{write} to your repository in order for them -to be able to clone it or pull changes from it; they only need -\emph{read} permission. (This is \emph{not} a common feature among -revision control systems, so don't take it for granted! Most require -readers to be able to lock a repository to access it safely, and this -requires write permission on at least one directory, which of course -makes for all kinds of nasty and annoying security and administrative -problems.) - -Mercurial uses locks to ensure that only one process can write to a -repository at a time (the locking mechanism is safe even over -filesystems that are notoriously hostile to locking, such as NFS). If -a repository is locked, a writer will wait for a while to retry if the -repository becomes unlocked, but if the repository remains locked for -too long, the process attempting to write will time out after a while. -This means that your daily automated scripts won't get stuck forever -and pile up if a system crashes unnoticed, for example. (Yes, the -timeout is configurable, from zero to infinity.) - -\subsubsection{Safe dirstate access} - -As with revision data, Mercurial doesn't take a lock to read the -dirstate file; it does acquire a lock to write it. To avoid the -possibility of reading a partially written copy of the dirstate file, -Mercurial writes to a file with a unique name in the same directory as -the dirstate file, then renames the temporary file atomically to -\filename{dirstate}. The file named \filename{dirstate} is thus -guaranteed to be complete, not partially written. - -\subsection{Avoiding seeks} - -Critical to Mercurial's performance is the avoidance of seeks of the -disk head, since any seek is far more expensive than even a -comparatively large read operation. - -This is why, for example, the dirstate is stored in a single file. If -there were a dirstate file per directory that Mercurial tracked, the -disk would seek once per directory. Instead, Mercurial reads the -entire single dirstate file in one step. - -Mercurial also uses a ``copy on write'' scheme when cloning a -repository on local storage. Instead of copying every revlog file -from the old repository into the new repository, it makes a ``hard -link'', which is a shorthand way to say ``these two names point to the -same file''. When Mercurial is about to write to one of a revlog's -files, it checks to see if the number of names pointing at the file is -greater than one. If it is, more than one repository is using the -file, so Mercurial makes a new copy of the file that is private to -this repository. - -A few revision control developers have pointed out that this idea of -making a complete private copy of a file is not very efficient in its -use of storage. While this is true, storage is cheap, and this method -gives the highest performance while deferring most book-keeping to the -operating system. An alternative scheme would most likely reduce -performance and increase the complexity of the software, each of which -is much more important to the ``feel'' of day-to-day use. - -\subsection{Other contents of the dirstate} - -Because Mercurial doesn't force you to tell it when you're modifying a -file, it uses the dirstate to store some extra information so it can -determine efficiently whether you have modified a file. For each file -in the working directory, it stores the time that it last modified the -file itself, and the size of the file at that time. - -When you explicitly \hgcmd{add}, \hgcmd{remove}, \hgcmd{rename} or -\hgcmd{copy} files, Mercurial updates the dirstate so that it knows -what to do with those files when you commit. - -When Mercurial is checking the states of files in the working -directory, it first checks a file's modification time. If that has -not changed, the file must not have been modified. If the file's size -has changed, the file must have been modified. If the modification -time has changed, but the size has not, only then does Mercurial need -to read the actual contents of the file to see if they've changed. -Storing these few extra pieces of information dramatically reduces the -amount of data that Mercurial needs to read, which yields large -performance improvements compared to other revision control systems. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/daily.tex --- a/fr/daily.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,381 +0,0 @@ -\chapter{Mercurial in daily use} -\label{chap:daily} - -\section{Telling Mercurial which files to track} - -Mercurial does not work with files in your repository unless you tell -it to manage them. The \hgcmd{status} command will tell you which -files Mercurial doesn't know about; it uses a ``\texttt{?}'' to -display such files. - -To tell Mercurial to track a file, use the \hgcmd{add} command. Once -you have added a file, the entry in the output of \hgcmd{status} for -that file changes from ``\texttt{?}'' to ``\texttt{A}''. -\interaction{daily.files.add} - -After you run a \hgcmd{commit}, the files that you added before the -commit will no longer be listed in the output of \hgcmd{status}. The -reason for this is that \hgcmd{status} only tells you about -``interesting'' files---those that you have modified or told Mercurial -to do something with---by default. If you have a repository that -contains thousands of files, you will rarely want to know about files -that Mercurial is tracking, but that have not changed. (You can still -get this information; we'll return to this later.) - -Once you add a file, Mercurial doesn't do anything with it -immediately. Instead, it will take a snapshot of the file's state the -next time you perform a commit. It will then continue to track the -changes you make to the file every time you commit, until you remove -the file. - -\subsection{Explicit versus implicit file naming} - -A useful behaviour that Mercurial has is that if you pass the name of -a directory to a command, every Mercurial command will treat this as -``I want to operate on every file in this directory and its -subdirectories''. -\interaction{daily.files.add-dir} -Notice in this example that Mercurial printed the names of the files -it added, whereas it didn't do so when we added the file named -\filename{a} in the earlier example. - -What's going on is that in the former case, we explicitly named the -file to add on the command line, so the assumption that Mercurial -makes in such cases is that you know what you were doing, and it -doesn't print any output. - -However, when we \emph{imply} the names of files by giving the name of -a directory, Mercurial takes the extra step of printing the name of -each file that it does something with. This makes it more clear what -is happening, and reduces the likelihood of a silent and nasty -surprise. This behaviour is common to most Mercurial commands. - -\subsection{Aside: Mercurial tracks files, not directories} - -Mercurial does not track directory information. Instead, it tracks -the path to a file. Before creating a file, it first creates any -missing directory components of the path. After it deletes a file, it -then deletes any empty directories that were in the deleted file's -path. This sounds like a trivial distinction, but it has one minor -practical consequence: it is not possible to represent a completely -empty directory in Mercurial. - -Empty directories are rarely useful, and there are unintrusive -workarounds that you can use to achieve an appropriate effect. The -developers of Mercurial thus felt that the complexity that would be -required to manage empty directories was not worth the limited benefit -this feature would bring. - -If you need an empty directory in your repository, there are a few -ways to achieve this. One is to create a directory, then \hgcmd{add} a -``hidden'' file to that directory. On Unix-like systems, any file -name that begins with a period (``\texttt{.}'') is treated as hidden -by most commands and GUI tools. This approach is illustrated in -figure~\ref{ex:daily:hidden}. - -\begin{figure}[ht] - \interaction{daily.files.hidden} - \caption{Simulating an empty directory using a hidden file} - \label{ex:daily:hidden} -\end{figure} - -Another way to tackle a need for an empty directory is to simply -create one in your automated build scripts before they will need it. - -\section{How to stop tracking a file} - -Once you decide that a file no longer belongs in your repository, use -the \hgcmd{remove} command; this deletes the file, and tells Mercurial -to stop tracking it. A removed file is represented in the output of -\hgcmd{status} with a ``\texttt{R}''. -\interaction{daily.files.remove} - -After you \hgcmd{remove} a file, Mercurial will no longer track -changes to that file, even if you recreate a file with the same name -in your working directory. If you do recreate a file with the same -name and want Mercurial to track the new file, simply \hgcmd{add} it. -Mercurial will know that the newly added file is not related to the -old file of the same name. - -\subsection{Removing a file does not affect its history} - -It is important to understand that removing a file has only two -effects. -\begin{itemize} -\item It removes the current version of the file from the working - directory. -\item It stops Mercurial from tracking changes to the file, from the - time of the next commit. -\end{itemize} -Removing a file \emph{does not} in any way alter the \emph{history} of -the file. - -If you update the working directory to a changeset in which a file -that you have removed was still tracked, it will reappear in the -working directory, with the contents it had when you committed that -changeset. If you then update the working directory to a later -changeset, in which the file had been removed, Mercurial will once -again remove the file from the working directory. - -\subsection{Missing files} - -Mercurial considers a file that you have deleted, but not used -\hgcmd{remove} to delete, to be \emph{missing}. A missing file is -represented with ``\texttt{!}'' in the output of \hgcmd{status}. -Mercurial commands will not generally do anything with missing files. -\interaction{daily.files.missing} - -If your repository contains a file that \hgcmd{status} reports as -missing, and you want the file to stay gone, you can run -\hgcmdargs{remove}{\hgopt{remove}{--after}} at any time later on, to -tell Mercurial that you really did mean to remove the file. -\interaction{daily.files.remove-after} - -On the other hand, if you deleted the missing file by accident, use -\hgcmdargs{revert}{\emph{filename}} to recover the file. It will -reappear, in unmodified form. -\interaction{daily.files.recover-missing} - -\subsection{Aside: why tell Mercurial explicitly to - remove a file?} - -You might wonder why Mercurial requires you to explicitly tell it that -you are deleting a file. Early during the development of Mercurial, -it let you delete a file however you pleased; Mercurial would notice -the absence of the file automatically when you next ran a -\hgcmd{commit}, and stop tracking the file. In practice, this made it -too easy to accidentally remove a file without noticing. - -\subsection{Useful shorthand---adding and removing files - in one step} - -Mercurial offers a combination command, \hgcmd{addremove}, that adds -untracked files and marks missing files as removed. -\interaction{daily.files.addremove} -The \hgcmd{commit} command also provides a \hgopt{commit}{-A} option -that performs this same add-and-remove, immediately followed by a -commit. -\interaction{daily.files.commit-addremove} - -\section{Copying files} - -Mercurial provides a \hgcmd{copy} command that lets you make a new -copy of a file. When you copy a file using this command, Mercurial -makes a record of the fact that the new file is a copy of the original -file. It treats these copied files specially when you merge your work -with someone else's. - -\subsection{The results of copying during a merge} - -What happens during a merge is that changes ``follow'' a copy. To -best illustrate what this means, let's create an example. We'll start -with the usual tiny repository that contains a single file. -\interaction{daily.copy.init} -We need to do some work in parallel, so that we'll have something to -merge. So let's clone our repository. -\interaction{daily.copy.clone} -Back in our initial repository, let's use the \hgcmd{copy} command to -make a copy of the first file we created. -\interaction{daily.copy.copy} - -If we look at the output of the \hgcmd{status} command afterwards, the -copied file looks just like a normal added file. -\interaction{daily.copy.status} -But if we pass the \hgopt{status}{-C} option to \hgcmd{status}, it -prints another line of output: this is the file that our newly-added -file was copied \emph{from}. -\interaction{daily.copy.status-copy} - -Now, back in the repository we cloned, let's make a change in -parallel. We'll add a line of content to the original file that we -created. -\interaction{daily.copy.other} -Now we have a modified \filename{file} in this repository. When we -pull the changes from the first repository, and merge the two heads, -Mercurial will propagate the changes that we made locally to -\filename{file} into its copy, \filename{new-file}. -\interaction{daily.copy.merge} - -\subsection{Why should changes follow copies?} -\label{sec:daily:why-copy} - -This behaviour, of changes to a file propagating out to copies of the -file, might seem esoteric, but in most cases it's highly desirable. - -First of all, remember that this propagation \emph{only} happens when -you merge. So if you \hgcmd{copy} a file, and subsequently modify the -original file during the normal course of your work, nothing will -happen. - -The second thing to know is that modifications will only propagate -across a copy as long as the repository that you're pulling changes -from \emph{doesn't know} about the copy. - -The reason that Mercurial does this is as follows. Let's say I make -an important bug fix in a source file, and commit my changes. -Meanwhile, you've decided to \hgcmd{copy} the file in your repository, -without knowing about the bug or having seen the fix, and you have -started hacking on your copy of the file. - -If you pulled and merged my changes, and Mercurial \emph{didn't} -propagate changes across copies, your source file would now contain -the bug, and unless you remembered to propagate the bug fix by hand, -the bug would \emph{remain} in your copy of the file. - -By automatically propagating the change that fixed the bug from the -original file to the copy, Mercurial prevents this class of problem. -To my knowledge, Mercurial is the \emph{only} revision control system -that propagates changes across copies like this. - -Once your change history has a record that the copy and subsequent -merge occurred, there's usually no further need to propagate changes -from the original file to the copied file, and that's why Mercurial -only propagates changes across copies until this point, and no -further. - -\subsection{How to make changes \emph{not} follow a copy} - -If, for some reason, you decide that this business of automatically -propagating changes across copies is not for you, simply use your -system's normal file copy command (on Unix-like systems, that's -\command{cp}) to make a copy of a file, then \hgcmd{add} the new copy -by hand. Before you do so, though, please do reread -section~\ref{sec:daily:why-copy}, and make an informed decision that -this behaviour is not appropriate to your specific case. - -\subsection{Behaviour of the \hgcmd{copy} command} - -When you use the \hgcmd{copy} command, Mercurial makes a copy of each -source file as it currently stands in the working directory. This -means that if you make some modifications to a file, then \hgcmd{copy} -it without first having committed those changes, the new copy will -also contain the modifications you have made up until that point. (I -find this behaviour a little counterintuitive, which is why I mention -it here.) - -The \hgcmd{copy} command acts similarly to the Unix \command{cp} -command (you can use the \hgcmd{cp} alias if you prefer). The last -argument is the \emph{destination}, and all prior arguments are -\emph{sources}. If you pass it a single file as the source, and the -destination does not exist, it creates a new file with that name. -\interaction{daily.copy.simple} -If the destination is a directory, Mercurial copies its sources into -that directory. -\interaction{daily.copy.dir-dest} -Copying a directory is recursive, and preserves the directory -structure of the source. -\interaction{daily.copy.dir-src} -If the source and destination are both directories, the source tree is -recreated in the destination directory. -\interaction{daily.copy.dir-src-dest} - -As with the \hgcmd{rename} command, if you copy a file manually and -then want Mercurial to know that you've copied the file, simply use -the \hgopt{copy}{--after} option to \hgcmd{copy}. -\interaction{daily.copy.after} - -\section{Renaming files} - -It's rather more common to need to rename a file than to make a copy -of it. The reason I discussed the \hgcmd{copy} command before talking -about renaming files is that Mercurial treats a rename in essentially -the same way as a copy. Therefore, knowing what Mercurial does when -you copy a file tells you what to expect when you rename a file. - -When you use the \hgcmd{rename} command, Mercurial makes a copy of -each source file, then deletes it and marks the file as removed. -\interaction{daily.rename.rename} -The \hgcmd{status} command shows the newly copied file as added, and -the copied-from file as removed. -\interaction{daily.rename.status} -As with the results of a \hgcmd{copy}, we must use the -\hgopt{status}{-C} option to \hgcmd{status} to see that the added file -is really being tracked by Mercurial as a copy of the original, now -removed, file. -\interaction{daily.rename.status-copy} - -As with \hgcmd{remove} and \hgcmd{copy}, you can tell Mercurial about -a rename after the fact using the \hgopt{rename}{--after} option. In -most other respects, the behaviour of the \hgcmd{rename} command, and -the options it accepts, are similar to the \hgcmd{copy} command. - -\subsection{Renaming files and merging changes} - -Since Mercurial's rename is implemented as copy-and-remove, the same -propagation of changes happens when you merge after a rename as after -a copy. - -If I modify a file, and you rename it to a new name, and then we merge -our respective changes, my modifications to the file under its -original name will be propagated into the file under its new name. -(This is something you might expect to ``simply work,'' but not all -revision control systems actually do this.) - -Whereas having changes follow a copy is a feature where you can -perhaps nod and say ``yes, that might be useful,'' it should be clear -that having them follow a rename is definitely important. Without -this facility, it would simply be too easy for changes to become -orphaned when files are renamed. - -\subsection{Divergent renames and merging} - -The case of diverging names occurs when two developers start with a -file---let's call it \filename{foo}---in their respective -repositories. - -\interaction{rename.divergent.clone} -Anne renames the file to \filename{bar}. -\interaction{rename.divergent.rename.anne} -Meanwhile, Bob renames it to \filename{quux}. -\interaction{rename.divergent.rename.bob} - -I like to think of this as a conflict because each developer has -expressed different intentions about what the file ought to be named. - -What do you think should happen when they merge their work? -Mercurial's actual behaviour is that it always preserves \emph{both} -names when it merges changesets that contain divergent renames. -\interaction{rename.divergent.merge} - -Notice that Mercurial does warn about the divergent renames, but it -leaves it up to you to do something about the divergence after the merge. - -\subsection{Convergent renames and merging} - -Another kind of rename conflict occurs when two people choose to -rename different \emph{source} files to the same \emph{destination}. -In this case, Mercurial runs its normal merge machinery, and lets you -guide it to a suitable resolution. - -\subsection{Other name-related corner cases} - -Mercurial has a longstanding bug in which it fails to handle a merge -where one side has a file with a given name, while another has a -directory with the same name. This is documented as~\bug{29}. -\interaction{issue29.go} - -\section{Recovering from mistakes} - -Mercurial has some useful commands that will help you to recover from -some common mistakes. - -The \hgcmd{revert} command lets you undo changes that you have made to -your working directory. For example, if you \hgcmd{add} a file by -accident, just run \hgcmd{revert} with the name of the file you added, -and while the file won't be touched in any way, it won't be tracked -for adding by Mercurial any longer, either. You can also use -\hgcmd{revert} to get rid of erroneous changes to a file. - -It's useful to remember that the \hgcmd{revert} command is useful for -changes that you have not yet committed. Once you've committed a -change, if you decide it was a mistake, you can still do something -about it, though your options may be more limited. - -For more information about the \hgcmd{revert} command, and details -about how to deal with changes you have already committed, see -chapter~\ref{chap:undo}. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/examples/auto-snippets.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/examples/auto-snippets.xml Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/examples/data/check_whitespace.py --- a/fr/examples/data/check_whitespace.py Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -#!/usr/bin/python - -import re - -def trailing_whitespace(difflines): - added, linenum, header = [], 0, False - - for line in difflines: - if header: - # remember the name of the file that this diff affects - m = re.match(r'(?:---|\+\+\+) ([^\t]+)', line) - if m and m.group(1) != '/dev/null': - filename = m.group(1).split('/', 1)[-1] - if line.startswith('+++ '): - header = False - continue - if line.startswith('diff '): - header = True - continue - # hunk header - save the line number - m = re.match(r'@@ -\d+,\d+ \+(\d+),', line) - if m: - linenum = int(m.group(1)) - continue - # hunk body - check for an added line with trailing whitespace - m = re.match(r'\+.*\s$', line) - if m: - added.append((filename, linenum)) - if line and line[0] in ' +': - linenum += 1 - return added - -if __name__ == '__main__': - import os, sys - - added = trailing_whitespace(os.popen('hg export tip')) - if added: - for filename, linenum in added: - print >> sys.stderr, ('%s, line %d: trailing whitespace added' % - (filename, linenum)) - # save the commit message so we don't need to retype it - os.system('hg tip --template "{desc}" > .hg/commit.save') - print >> sys.stderr, 'commit message saved to .hg/commit.save' - sys.exit(1) diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/examples/hook.ws --- a/fr/examples/hook.ws Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -#!/bin/bash - -hg init a -cd a -echo '[hooks]' > .hg/hgrc -echo "pretxncommit.whitespace = hg export tip | (! egrep -q '^\\+.*[ \\t]$')" >> .hg/hgrc - -#$ name: simple - -cat .hg/hgrc -echo 'a ' > a -hg commit -A -m 'test with trailing whitespace' -echo 'a' > a -hg commit -A -m 'drop trailing whitespace and try again' - -#$ name: - -echo '[hooks]' > .hg/hgrc -echo "pretxncommit.whitespace = .hg/check_whitespace.py" >> .hg/hgrc -cp $EXAMPLE_DIR/data/check_whitespace.py .hg - -#$ name: better - -cat .hg/hgrc -echo 'a ' >> a -hg commit -A -m 'add new line with trailing whitespace' -sed -i 's, *$,,' a -hg commit -A -m 'trimmed trailing whitespace' - -#$ name: -exit 0 diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/feature-branches.dot --- a/fr/feature-branches.dot Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -digraph feature_branches { - master -> crypto; - master -> filesystems; - master -> ipc; - master -> memory; - master -> network; - master -> security; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/bad-merge-1.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/bad-merge-1.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,13 @@ +digraph bad_merge_1 { + ancestor [label="1: ancestor"]; + left [label="2: my change"]; + right [label="3: your change"]; + bad [label="4: bad merge"]; + new [label="5: new change"]; + + ancestor -> left; + ancestor -> right; + left -> bad; + right -> bad; + bad -> new; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/bad-merge-2.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/bad-merge-2.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,18 @@ +digraph bad_merge_2 { + ancestor [label="1: ancestor",color=grey,fontcolor=grey]; + left [label="2: my change",color=grey,fontcolor=grey]; + right [label="3: your change",color=grey,fontcolor=grey]; + bad [label="4: bad merge",color=grey,fontcolor=grey]; + new [label="5: new change",color=grey,fontcolor=grey]; + + bak_left [label="6: backout 1 of\nbad merge",shape=box]; + + ancestor -> left [color=grey]; + ancestor -> right [color=grey]; + left -> bad [color=grey]; + right -> bad [color=grey]; + bad -> new [color=grey]; + + bad -> bak_left; + left -> bak_left [style=dotted,label="--parent=2"]; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/bad-merge-3.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/bad-merge-3.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,22 @@ +digraph bad_merge_3 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left [label="6: backout 1 of\nbad merge",color=grey,shape=box]; + bak_right [label="8: backout 2 of\nbad merge",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color=grey]; + left -> bak_left [style=dotted,label="--parent=2",color=grey,fontcolor=grey]; + + bad -> bak_right; + right -> bak_right [style=dotted,label="--parent=3"]; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/bad-merge-4.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/bad-merge-4.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,26 @@ +digraph bad_merge_4 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left [label="6: backout 1 of\nbad merge",color=grey,fontcolor=grey,shape=box]; + bak_right [label="7: backout 2 of\nbad merge",color=grey,fontcolor=grey,shape=box]; + good [label="8: merge\nof backouts",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color=grey]; + left -> bak_left [style=dotted,label="--parent=2",color=grey,fontcolor=grey]; + + bad -> bak_right [color=grey]; + right -> bak_right [style=dotted,label="--parent=3",color=grey,fontcolor=grey]; + + bak_left -> good; + bak_right -> good; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/bad-merge-5.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/bad-merge-5.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,30 @@ +digraph bad_merge_5 { + ancestor [label="1: ancestor",color="#bbbbbb",fontcolor="#bbbbbb"]; + left [label="2: my change",color="#bbbbbb",fontcolor="#bbbbbb"]; + right [label="3: your change",color="#bbbbbb",fontcolor="#bbbbbb"]; + bad [label="4: bad merge",color="#bbbbbb",fontcolor="#bbbbbb"]; + new [label="5: new change",color=grey,fontcolor=grey]; + + bak_left [label="6: backout 1 of\nbad merge",color="#bbbbbb",fontcolor="#bbbbbb",shape=box]; + bak_right [label="7: backout 2 of\nbad merge",color="#bbbbbb",fontcolor="#bbbbbb",shape=box]; + good [label="8: merge\nof backouts",color=grey,fontcolor=grey,shape=box]; + last [label="9: merge with\nnew change",shape=box]; + + ancestor -> left [color="#bbbbbb"]; + ancestor -> right [color="#bbbbbb"]; + left -> bad [color="#bbbbbb"]; + right -> bad [color="#bbbbbb"]; + bad -> new [color="#bbbbbb"]; + + bad -> bak_left [color="#bbbbbb"]; + left -> bak_left [style=dotted,label="--parent=2",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bad -> bak_right [color="#bbbbbb"]; + right -> bak_right [style=dotted,label="--parent=3",color="#bbbbbb",fontcolor="#bbbbbb"]; + + bak_left -> good [color=grey]; + bak_right -> good [color=grey]; + + good -> last; + new -> last; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/caution.png Binary file fr/figs/caution.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/feature-branches.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/feature-branches.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,8 @@ +digraph feature_branches { + master -> crypto; + master -> filesystems; + master -> ipc; + master -> memory; + master -> network; + master -> security; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/filelog.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + .hg/store/data/_r_e_a_d_m_e.i + + + + + README + + + + + + + + + .hg/store/data/src/hello.c.d + .hg/store/data/src/hello.c.i + + + + + src/hello.c + + + + Working directory + Repository + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/kdiff3.png Binary file fr/figs/kdiff3.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/metadata.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Changelog + Manifest + Filelogs + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/mq-stack.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,270 @@ + + + + + + + + + image/svg+xml + + + + + + + prevent-compiler-reorder.patch + + namespace-cleanup.patch + + powerpc-port-fixes.patch + + report-devinfo-correctly.patch + { + { + present in series,but not applied + patches applied,changesets present + topmostapplied patch + 201ad3209902 + 126b84e593ae + a655daf15409 + e50d59aaea3a + + forbid-illegal-params.patch + + fix-memory-leak.patch + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/note.png Binary file fr/figs/note.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/revlog.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + Second parent + 32bf9a5f22c0 + + + + Revision hash + 34b8b7a15ea1 + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + 000000000000 + + + + Second parent + 000000000000 + + + + + Revision hash + ff9dc8bc2a8b + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + 34b8b7a15ea1 + + + + Second parent + 000000000000 + + + + Revision hash + 1b67dc96f27a + + + + ... + Revision data (delta or snapshot) + + + + + + + + First parent + ff9dc8bc2a8b + + + + Second parent + 000000000000 + + + + Revision hash + 5b80c922ebdd + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + ecacb6b4c9fd + + + + Second parent + 000000000000 + + + + Revision hash + 32bf9a5f22c0 + + + + ... + Revision data (delta or snapshot) + + + + + + First parent + ff9dc8bc2a8b + + + + Second parent + 000000000000 + + + + Revision hash + ecacb6b4c9fd + + + + ... + Revision data (delta or snapshot) + + + + + + + Head revision(no children) + Merge revision(two parents) + Branches(two revisions,same parent) + + + First revision(both parents null) + + First parent + 5b80c922ebdd + + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/snapshot.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,202 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + Index, rev 7 + + Revlog index (.i file) + Revlog data (.d file) + + + Snapshot, rev 4 + + Delta, rev 4 to 5 + + Delta, rev 5 to 6 + + Delta, rev 6 to 7 + + Delta, rev 2 to 3 + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/throbber.gif Binary file fr/figs/throbber.gif has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tip.png Binary file fr/figs/tip.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/tour-history.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + (newest) + (oldest) + + 4: REV4 + + + revisionnumber + changesetidentifier + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/tour-merge-conflict.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,210 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Greetings!I am Mariam Abacha, the wife of former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + + + + Greetings!I am Shehu Musa Abacha, cousin to the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + + + + Greetings!I am Alhaji Abba Abacha, son of the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + Base version + Our changes + Their changes + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/tour-merge-merge.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip (and head) + head + + + + merge + working directoryduring merge + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip + + + 7: REV7_my_new_hello + Working directory during merge + Repository after merge committed + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/tour-merge-pull.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip (and head) + head + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/tour-merge-sep-repos.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,466 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_hello + + my-hello + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + my-new-hello + newest changesdiffer + common history + + + + + + + head revision(has no children) + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/undo-manual-merge.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,8 @@ +digraph undo_manual { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; + "third change" -> "manual\nmerge"; + backout -> "manual\nmerge"; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/undo-manual.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,6 @@ +digraph undo_manual { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/undo-non-tip.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,9 @@ +digraph undo_non_tip { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; + merge [label="automated\nmerge", shape=box]; + "third change" -> merge; + backout -> merge; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/undo-simple.dot Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,4 @@ +digraph undo_simple { + "first change" -> "second change"; + "second change" -> "back out\nsecond change"; +} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/warning.png Binary file fr/figs/warning.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/wdir-after-commit.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + dfbbb33f3fa3 + + + e7639888bb2f + + 7b064d8bac5e + + + + 000000000000 + + History in repository + + + + dfbbb33f3fa3 + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + Newchangeset + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/wdir-branch.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + + + ffb20e1701ea + + + Pre-existing head + Newly created head (and tip) + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/wdir-merge.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + e7639888bb2f + + First parent (unchanged) + Second parent + Parents of working directory + + + + + ffb20e1701ea + + + Pre-existing head + Newly created head (and tip) + + + + + + e7639888bb2f + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/wdir-pre-branch.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + e7639888bb2f + + + 7b064d8bac5e + + + + 000000000000 + + History in repository + + + + 7b064d8bac5e + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/figs/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/figs/wdir.svg Sun Aug 16 04:58:01 2009 +0200 @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + History in repository + + + + + e7639888bb2f + + + + 000000000000 + + First parent + Second parent + + Parents of working directory + + + + diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/filelog.svg --- a/fr/filelog.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,373 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - .hg/store/data/README.i - - - - - README - - - - - - - - - .hg/store/data/src/hello.c.d - .hg/store/data/src/hello.c.i - - - - - src/hello.c - - - - Working directory - Repository - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/filenames.tex --- a/fr/filenames.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,306 +0,0 @@ -\chapter{File names and pattern matching} -\label{chap:names} - -Mercurial provides mechanisms that let you work with file names in a -consistent and expressive way. - -\section{Simple file naming} - -Mercurial uses a unified piece of machinery ``under the hood'' to -handle file names. Every command behaves uniformly with respect to -file names. The way in which commands work with file names is as -follows. - -If you explicitly name real files on the command line, Mercurial works -with exactly those files, as you would expect. -\interaction{filenames.files} - -When you provide a directory name, Mercurial will interpret this as -``operate on every file in this directory and its subdirectories''. -Mercurial traverses the files and subdirectories in a directory in -alphabetical order. When it encounters a subdirectory, it will -traverse that subdirectory before continuing with the current -directory. -\interaction{filenames.dirs} - -\section{Running commands without any file names} - -Mercurial's commands that work with file names have useful default -behaviours when you invoke them without providing any file names or -patterns. What kind of behaviour you should expect depends on what -the command does. Here are a few rules of thumb you can use to -predict what a command is likely to do if you don't give it any names -to work with. -\begin{itemize} -\item Most commands will operate on the entire working directory. - This is what the \hgcmd{add} command does, for example. -\item If the command has effects that are difficult or impossible to - reverse, it will force you to explicitly provide at least one name - or pattern (see below). This protects you from accidentally - deleting files by running \hgcmd{remove} with no arguments, for - example. -\end{itemize} - -It's easy to work around these default behaviours if they don't suit -you. If a command normally operates on the whole working directory, -you can invoke it on just the current directory and its subdirectories -by giving it the name ``\dirname{.}''. -\interaction{filenames.wdir-subdir} - -Along the same lines, some commands normally print file names relative -to the root of the repository, even if you're invoking them from a -subdirectory. Such a command will print file names relative to your -subdirectory if you give it explicit names. Here, we're going to run -\hgcmd{status} from a subdirectory, and get it to operate on the -entire working directory while printing file names relative to our -subdirectory, by passing it the output of the \hgcmd{root} command. -\interaction{filenames.wdir-relname} - -\section{Telling you what's going on} - -The \hgcmd{add} example in the preceding section illustrates something -else that's helpful about Mercurial commands. If a command operates -on a file that you didn't name explicitly on the command line, it will -usually print the name of the file, so that you will not be surprised -what's going on. - -The principle here is of \emph{least surprise}. If you've exactly -named a file on the command line, there's no point in repeating it -back at you. If Mercurial is acting on a file \emph{implicitly}, -because you provided no names, or a directory, or a pattern (see -below), it's safest to tell you what it's doing. - -For commands that behave this way, you can silence them using the -\hggopt{-q} option. You can also get them to print the name of every -file, even those you've named explicitly, using the \hggopt{-v} -option. - -\section{Using patterns to identify files} - -In addition to working with file and directory names, Mercurial lets -you use \emph{patterns} to identify files. Mercurial's pattern -handling is expressive. - -On Unix-like systems (Linux, MacOS, etc.), the job of matching file -names to patterns normally falls to the shell. On these systems, you -must explicitly tell Mercurial that a name is a pattern. On Windows, -the shell does not expand patterns, so Mercurial will automatically -identify names that are patterns, and expand them for you. - -To provide a pattern in place of a regular name on the command line, -the mechanism is simple: -\begin{codesample2} - syntax:patternbody -\end{codesample2} -That is, a pattern is identified by a short text string that says what -kind of pattern this is, followed by a colon, followed by the actual -pattern. - -Mercurial supports two kinds of pattern syntax. The most frequently -used is called \texttt{glob}; this is the same kind of pattern -matching used by the Unix shell, and should be familiar to Windows -command prompt users, too. - -When Mercurial does automatic pattern matching on Windows, it uses -\texttt{glob} syntax. You can thus omit the ``\texttt{glob:}'' prefix -on Windows, but it's safe to use it, too. - -The \texttt{re} syntax is more powerful; it lets you specify patterns -using regular expressions, also known as regexps. - -By the way, in the examples that follow, notice that I'm careful to -wrap all of my patterns in quote characters, so that they won't get -expanded by the shell before Mercurial sees them. - -\subsection{Shell-style \texttt{glob} patterns} - -This is an overview of the kinds of patterns you can use when you're -matching on glob patterns. - -The ``\texttt{*}'' character matches any string, within a single -directory. -\interaction{filenames.glob.star} - -The ``\texttt{**}'' pattern matches any string, and crosses directory -boundaries. It's not a standard Unix glob token, but it's accepted by -several popular Unix shells, and is very useful. -\interaction{filenames.glob.starstar} - -The ``\texttt{?}'' pattern matches any single character. -\interaction{filenames.glob.question} - -The ``\texttt{[}'' character begins a \emph{character class}. This -matches any single character within the class. The class ends with a -``\texttt{]}'' character. A class may contain multiple \emph{range}s -of the form ``\texttt{a-f}'', which is shorthand for -``\texttt{abcdef}''. -\interaction{filenames.glob.range} -If the first character after the ``\texttt{[}'' in a character class -is a ``\texttt{!}'', it \emph{negates} the class, making it match any -single character not in the class. - -A ``\texttt{\{}'' begins a group of subpatterns, where the whole group -matches if any subpattern in the group matches. The ``\texttt{,}'' -character separates subpatterns, and ``\texttt{\}}'' ends the group. -\interaction{filenames.glob.group} - -\subsubsection{Watch out!} - -Don't forget that if you want to match a pattern in any directory, you -should not be using the ``\texttt{*}'' match-any token, as this will -only match within one directory. Instead, use the ``\texttt{**}'' -token. This small example illustrates the difference between the two. -\interaction{filenames.glob.star-starstar} - -\subsection{Regular expression matching with \texttt{re} patterns} - -Mercurial accepts the same regular expression syntax as the Python -programming language (it uses Python's regexp engine internally). -This is based on the Perl language's regexp syntax, which is the most -popular dialect in use (it's also used in Java, for example). - -I won't discuss Mercurial's regexp dialect in any detail here, as -regexps are not often used. Perl-style regexps are in any case -already exhaustively documented on a multitude of web sites, and in -many books. Instead, I will focus here on a few things you should -know if you find yourself needing to use regexps with Mercurial. - -A regexp is matched against an entire file name, relative to the root -of the repository. In other words, even if you're already in -subbdirectory \dirname{foo}, if you want to match files under this -directory, your pattern must start with ``\texttt{foo/}''. - -One thing to note, if you're familiar with Perl-style regexps, is that -Mercurial's are \emph{rooted}. That is, a regexp starts matching -against the beginning of a string; it doesn't look for a match -anywhere within the string. To match anywhere in a string, start -your pattern with ``\texttt{.*}''. - -\section{Filtering files} - -Not only does Mercurial give you a variety of ways to specify files; -it lets you further winnow those files using \emph{filters}. Commands -that work with file names accept two filtering options. -\begin{itemize} -\item \hggopt{-I}, or \hggopt{--include}, lets you specify a pattern - that file names must match in order to be processed. -\item \hggopt{-X}, or \hggopt{--exclude}, gives you a way to - \emph{avoid} processing files, if they match this pattern. -\end{itemize} -You can provide multiple \hggopt{-I} and \hggopt{-X} options on the -command line, and intermix them as you please. Mercurial interprets -the patterns you provide using glob syntax by default (but you can use -regexps if you need to). - -You can read a \hggopt{-I} filter as ``process only the files that -match this filter''. -\interaction{filenames.filter.include} -The \hggopt{-X} filter is best read as ``process only the files that -don't match this pattern''. -\interaction{filenames.filter.exclude} - -\section{Ignoring unwanted files and directories} - -XXX. - -\section{Case sensitivity} -\label{sec:names:case} - -If you're working in a mixed development environment that contains -both Linux (or other Unix) systems and Macs or Windows systems, you -should keep in the back of your mind the knowledge that they treat the -case (``N'' versus ``n'') of file names in incompatible ways. This is -not very likely to affect you, and it's easy to deal with if it does, -but it could surprise you if you don't know about it. - -Operating systems and filesystems differ in the way they handle the -\emph{case} of characters in file and directory names. There are -three common ways to handle case in names. -\begin{itemize} -\item Completely case insensitive. Uppercase and lowercase versions - of a letter are treated as identical, both when creating a file and - during subsequent accesses. This is common on older DOS-based - systems. -\item Case preserving, but insensitive. When a file or directory is - created, the case of its name is stored, and can be retrieved and - displayed by the operating system. When an existing file is being - looked up, its case is ignored. This is the standard arrangement on - Windows and MacOS. The names \filename{foo} and \filename{FoO} - identify the same file. This treatment of uppercase and lowercase - letters as interchangeable is also referred to as \emph{case - folding}. -\item Case sensitive. The case of a name is significant at all times. - The names \filename{foo} and {FoO} identify different files. This - is the way Linux and Unix systems normally work. -\end{itemize} - -On Unix-like systems, it is possible to have any or all of the above -ways of handling case in action at once. For example, if you use a -USB thumb drive formatted with a FAT32 filesystem on a Linux system, -Linux will handle names on that filesystem in a case preserving, but -insensitive, way. - -\subsection{Safe, portable repository storage} - -Mercurial's repository storage mechanism is \emph{case safe}. It -translates file names so that they can be safely stored on both case -sensitive and case insensitive filesystems. This means that you can -use normal file copying tools to transfer a Mercurial repository onto, -for example, a USB thumb drive, and safely move that drive and -repository back and forth between a Mac, a PC running Windows, and a -Linux box. - -\subsection{Detecting case conflicts} - -When operating in the working directory, Mercurial honours the naming -policy of the filesystem where the working directory is located. If -the filesystem is case preserving, but insensitive, Mercurial will -treat names that differ only in case as the same. - -An important aspect of this approach is that it is possible to commit -a changeset on a case sensitive (typically Linux or Unix) filesystem -that will cause trouble for users on case insensitive (usually Windows -and MacOS) users. If a Linux user commits changes to two files, one -named \filename{myfile.c} and the other named \filename{MyFile.C}, -they will be stored correctly in the repository. And in the working -directories of other Linux users, they will be correctly represented -as separate files. - -If a Windows or Mac user pulls this change, they will not initially -have a problem, because Mercurial's repository storage mechanism is -case safe. However, once they try to \hgcmd{update} the working -directory to that changeset, or \hgcmd{merge} with that changeset, -Mercurial will spot the conflict between the two file names that the -filesystem would treat as the same, and forbid the update or merge -from occurring. - -\subsection{Fixing a case conflict} - -If you are using Windows or a Mac in a mixed environment where some of -your collaborators are using Linux or Unix, and Mercurial reports a -case folding conflict when you try to \hgcmd{update} or \hgcmd{merge}, -the procedure to fix the problem is simple. - -Just find a nearby Linux or Unix box, clone the problem repository -onto it, and use Mercurial's \hgcmd{rename} command to change the -names of any offending files or directories so that they will no -longer cause case folding conflicts. Commit this change, \hgcmd{pull} -or \hgcmd{push} it across to your Windows or MacOS system, and -\hgcmd{update} to the revision with the non-conflicting names. - -The changeset with case-conflicting names will remain in your -project's history, and you still won't be able to \hgcmd{update} your -working directory to that changeset on a Windows or MacOS system, but -you can continue development unimpeded. - -\begin{note} - Prior to version~0.9.3, Mercurial did not use a case safe repository - storage mechanism, and did not detect case folding conflicts. If - you are using an older version of Mercurial on Windows or MacOS, I - strongly recommend that you upgrade. -\end{note} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/fixhtml.py --- a/fr/fixhtml.py Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -# This script attempts to work around some of the more bizarre and -# quirky behaviours of htlatex. -# -# - We've persuaded htlatex to produce UTF-8, which unfortunately -# causes it to use huge character sequences to represent even the -# safe 7-bit ASCII subset of UTF-8. We fix that up. -# -# - BUT we have to treat angle brackets (for example, redirections in -# shell script snippets) specially, otherwise they'll break the -# generated HTML. (Reported by Johannes Hoff.) -# -# - For some reason, htlatex gives a unique ID to each fancyvrb -# environment, which makes writing a sane, small CSS stylesheet -# impossible. We squish all those IDs down to nothing. - -import os -import sys -import re - -angle_re = re.compile(r'([CE];)') -unicode_re = re.compile(r'�([0-7][0-9A-F]);') -fancyvrb_re = re.compile(r'id="fancyvrb\d+"', re.I) -ligature_re = re.compile(r'ྰ([0-4]);') - -tmpsuffix = '.tmp.' + str(os.getpid()) - -def hide_angle(m): - return m.group(1).lower() - -def fix_ascii(m): - return chr(int(m.group(1), 16)) - -ligatures = ['ff', 'fi', 'fl', 'ffi', 'ffl'] - -def expand_ligature(m): - return ligatures[int(m.group(1))] - -for name in sys.argv[1:]: - tmpname = name + tmpsuffix - ofp = file(tmpname, 'w') - for line in file(name): - line = angle_re.sub(hide_angle, line) - line = unicode_re.sub(fix_ascii, line) - line = ligature_re.sub(expand_ligature, line) - line = fancyvrb_re.sub('id="fancyvrb"', line) - ofp.write(line) - ofp.close() - os.rename(tmpname, name) diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/hgbook.css --- a/fr/hgbook.css Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -body { - font: 12px/1.5 Verdana, sans-serif; - padding-top: 50px; - padding-left: 80px; - padding-right: 80px; - padding-bottom: 90px; -} -.ptmr7t- { - font-family: monospace; -} -.ptmr7t-x-x-172 { - font-size: 172%; - font-family: monospace; -} -.ptmr7t-x-x-120 { - font-size: 120%; -} -.zpzccmry-x-x-120 { - font-size: 120%; - font-weight: bold; - font-style: italic; -} -.zpzccmry-x-x-120 { - font-weight: bold; - font-style: italic; -} -.pcrr7tn- { - font-family: monospace; -} -.ptmri7t- { - font-style: italic; -} -.ptmr7t-x-x-50 { - font-size: 50%; - font-family: monospace; -} -.ptmb7t- { - font-weight: bold; -} -.zptmcmr- { - font-style: italic; -} -.zptmcmrm- { - font-style: italic; -} -.zpzccmry- { - font-weight: bold; - font-style: italic; -} -.pcrb7t- { - font-family: monospace; - font-weight: bold; -} -.pcrro7t- { - font-family: monospace; - font-style: oblique; -} -p.noindent { - text-indent: 0em; - margin: 0em; -} -p.nopar { - text-indent: 0em; -} -p.indent { - text-indent: 1.5em; - margin: 0em; -} -a img { - border-top: 0; - border-left: 0; - border-right: 0; -} -center { - margin-top: 1em; - margin-bottom: 1em; -} -td center { - margin-top: 0em; - margin-bottom: 0em; -} -.Canvas { - position: relative; -} -img.math { - vertical-align: middle; -} -li p.indent { - text-indent: 0em; -} -.enumerate1 { - list-style-type: decimal; -} -.enumerate2 { - list-style-type: lower-alpha; -} -.enumerate3 { - list-style-type: lower-roman; -} -.enumerate4 { - list-style-type: upper-alpha; -} -div.newtheorem { - margin-bottom: 2em; - margin-top: 2em; -} -.obeylines-h,.obeylines-v { - white-space: nowrap; -} -div.obeylines-v p { - margin-top: 0; - margin-bottom: 0; -} -.overline { - text-decoration: overline; -} -.overline img { - border-top: 1px solid black; -} -td.displaylines { - text-align: center; - white-space: nowrap; -} -.centerline { - text-align: center; -} -.rightline { - text-align: right; -} -div.verbatim { - font-family: monospace; - white-space: nowrap; -} -table.verbatim { - width: 100%; -} -.fbox { - background: url(note.png) no-repeat #cec; - padding-left: 65px; - padding-top: 1em; - padding-bottom: 1em; - padding-right: 1em; - text-indent: 0pt; - border: dotted black 1px; -} -div.center div.fbox { - text-align: center; - clear: both; - padding-left: 3.0pt; - padding-right: 3.0pt; - text-indent: 0pt; - border: solid black 0.4pt; -} -table.minipage { - width: 100%; -} -div.center, div.center div.center { - text-align: center; - margin-left: 1em; - margin-right: 1em; -} -div.center div { - text-align: left; -} -div.flushright, div.flushright div.flushright { - text-align: right; -} -div.flushright div { - text-align: left; -} -div.flushleft { - text-align: left; -} -.underline { - text-decoration: underline; -} -.underline img { - border-bottom: 1px solid black; - margin-bottom: 1pt; -} -.framebox-c, .framebox-l, .framebox-r { - padding-left: 3.0pt; - padding-right: 3.0pt; - text-indent: 0pt; - border: solid black 0.4pt; -} -.framebox-c { - text-align: center; -} -.framebox-l { - text-align: left; -} -.framebox-r { - text-align: right; -} -span.thank-mark { - vertical-align: super -} -span.footnote-mark sup.textsuperscript, span.footnote-mark a sup.textsuperscript { - font-size: 80%; -} -div.tabular, div.center div.tabular { - text-align: center; - margin-top: 0.5em; - margin-bottom: 0.5em; -} -table.tabular td p { - margin-top: 0em; -} -table.tabular { - margin-left: auto; - margin-right: auto; -} -div.td00 { - margin-left: 0pt; - margin-right: 0pt; -} -div.td01 { - margin-left: 0pt; - margin-right: 5pt; -} -div.td10 { - margin-left: 5pt; - margin-right: 0pt; -} -div.td11 { - margin-left: 5pt; - margin-right: 5pt; -} -table[rules] { - border-left: solid black 0.4pt; - border-right: solid black 0.4pt; -} -td.td00 { - padding-left: 0pt; - padding-right: 0pt; -} -td.td01 { - padding-left: 0pt; - padding-right: 5pt; -} -td.td10 { - padding-left: 5pt; - padding-right: 0pt; -} -td.td11 { - padding-left: 5pt; - padding-right: 5pt; -} -table[rules] { - border-left: solid black 0.4pt; - border-right: solid black 0.4pt; -} -.hline hr, .cline hr { - height : 1px; - margin: 0px; -} -.tabbing-right { - text-align: right; -} -span.TEX { - letter-spacing: -0.125em; -} -span.TEX span.E { - position: relative;top: 0.5ex;left: -0.0417em; -} -a span.TEX span.E { - text-decoration: none; -} -span.LATEX span.A { - position: relative; - top: -0.5ex; - left: -0.4em; - font-size: 85%; -} -span.LATEX span.TEX { - position: relative; - left: -0.4em; -} -div.float img, div.float .caption { - text-align: center; -} -div.figure img, div.figure .caption { - text-align: center; -} -.marginpar { - width: 20%; - float: right; - text-align: left; - margin-left: auto; - margin-top: 0.5em; - font-size: 85%; - text-decoration: underline; -} -.marginpar p { - margin-top: 0.4em; - margin-bottom: 0.4em; -} -table.equation { - width: 100%; -} -.equation td { - text-align: center; -} -td.equation { - margin-top: 1em; - margin-bottom: 1em; -} -td.equation-label { - width: 5%; - text-align: center; -} -td.eqnarray4 { - width: 5%; - white-space: normal; -} -td.eqnarray2 { - width: 5%; -} -table.eqnarray-star, table.eqnarray { - width: 100%; -} -div.eqnarray { - text-align: center; -} -div.array { - text-align: center; -} -div.pmatrix { - text-align: center; -} -table.pmatrix { - width: 100%; -} -span.pmatrix img { - vertical-align: middle; -} -div.pmatrix { - text-align: center; -} -table.pmatrix { - width: 100%; -} -img.cdots { - vertical-align: middle; -} -.partToc a, .partToc, .likepartToc a, .likepartToc { - line-height: 200%; - font-weight: bold; - font-size: 110%; -} -.chapterToc a, .chapterToc, .likechapterToc a, .likechapterToc, .appendixToc a, .appendixToc { - line-height: 200%; - font-weight: bold; -} -.caption td.id { - font-weight: bold; - white-space: nowrap; -} -table.caption { - text-align: center; -} -h1.partHead { - text-align: center; -} -p.bibitem { - text-indent: -2em; - margin-left: 2em; - margin-top: 0.6em; - margin-bottom: 0.6em; -} -p.bibitem-p { - text-indent: 0em; - margin-left: 2em; - margin-top: 0.6em; - margin-bottom: 0.6em; -} -.paragraphHead, .likeparagraphHead { - margin-top: 2em; - font-weight: bold; -} -.subparagraphHead, .likesubparagraphHead { - font-weight: bold; -} -.quote { - margin-bottom: 0.25em; - margin-top: 0.25em; - margin-left: 1em; - margin-right: 1em; - text-align: justify; -} -.verse { - white-space: nowrap; - margin-left: 2em} -div.maketitle { - text-align: center; -} -h2.titleHead { - text-align: center; -} -div.maketitle { - margin-bottom: 2em; -} -div.author, div.date { - text-align: center; -} -div.thanks { - text-align: left; - margin-left: 10%; - font-size: 85%; - font-style: italic; -} -div.author { - white-space: nowrap; -} -.quotation { - margin-bottom: 0.25em; - margin-top: 0.25em; - margin-left: 1em; -} -h1.partHead { - text-align: center; -} -img.graphics { - margin-left: 10%; -} -.figure { - width: 100%; -} -P.fancyvrb { - white-space: nowrap; -} -hr { - border: 0; - height: 1px; -} -div#fancyvrb { - white-space: nowrap; - background: #eee; - padding: 1em; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/hgext.tex --- a/fr/hgext.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ -\chapter{Adding functionality with extensions} -\label{chap:hgext} - -While the core of Mercurial is quite complete from a functionality -standpoint, it's deliberately shorn of fancy features. This approach -of preserving simplicity keeps the software easy to deal with for both -maintainers and users. - -However, Mercurial doesn't box you in with an inflexible command set: -you can add features to it as \emph{extensions} (sometimes known as -\emph{plugins}). We've already discussed a few of these extensions in -earlier chapters. -\begin{itemize} -\item Section~\ref{sec:tour-merge:fetch} covers the \hgext{fetch} - extension; this combines pulling new changes and merging them with - local changes into a single command, \hgxcmd{fetch}{fetch}. -\item In chapter~\ref{chap:hook}, we covered several extensions that - are useful for hook-related functionality: \hgext{acl} adds access - control lists; \hgext{bugzilla} adds integration with the Bugzilla - bug tracking system; and \hgext{notify} sends notification emails on - new changes. -\item The Mercurial Queues patch management extension is so invaluable - that it merits two chapters and an appendix all to itself. - Chapter~\ref{chap:mq} covers the basics; - chapter~\ref{chap:mq-collab} discusses advanced topics; and - appendix~\ref{chap:mqref} goes into detail on each command. -\end{itemize} - -In this chapter, we'll cover some of the other extensions that are -available for Mercurial, and briefly touch on some of the machinery -you'll need to know about if you want to write an extension of your -own. -\begin{itemize} -\item In section~\ref{sec:hgext:inotify}, we'll discuss the - possibility of \emph{huge} performance improvements using the - \hgext{inotify} extension. -\end{itemize} - -\section{Improve performance with the \hgext{inotify} extension} -\label{sec:hgext:inotify} - -Are you interested in having some of the most common Mercurial -operations run as much as a hundred times faster? Read on! - -Mercurial has great performance under normal circumstances. For -example, when you run the \hgcmd{status} command, Mercurial has to -scan almost every directory and file in your repository so that it can -display file status. Many other Mercurial commands need to do the -same work behind the scenes; for example, the \hgcmd{diff} command -uses the status machinery to avoid doing an expensive comparison -operation on files that obviously haven't changed. - -Because obtaining file status is crucial to good performance, the -authors of Mercurial have optimised this code to within an inch of its -life. However, there's no avoiding the fact that when you run -\hgcmd{status}, Mercurial is going to have to perform at least one -expensive system call for each managed file to determine whether it's -changed since the last time Mercurial checked. For a sufficiently -large repository, this can take a long time. - -To put a number on the magnitude of this effect, I created a -repository containing 150,000 managed files. I timed \hgcmd{status} -as taking ten seconds to run, even when \emph{none} of those files had -been modified. - -Many modern operating systems contain a file notification facility. -If a program signs up to an appropriate service, the operating system -will notify it every time a file of interest is created, modified, or -deleted. On Linux systems, the kernel component that does this is -called \texttt{inotify}. - -Mercurial's \hgext{inotify} extension talks to the kernel's -\texttt{inotify} component to optimise \hgcmd{status} commands. The -extension has two components. A daemon sits in the background and -receives notifications from the \texttt{inotify} subsystem. It also -listens for connections from a regular Mercurial command. The -extension modifies Mercurial's behaviour so that instead of scanning -the filesystem, it queries the daemon. Since the daemon has perfect -information about the state of the repository, it can respond with a -result instantaneously, avoiding the need to scan every directory and -file in the repository. - -Recall the ten seconds that I measured plain Mercurial as taking to -run \hgcmd{status} on a 150,000 file repository. With the -\hgext{inotify} extension enabled, the time dropped to 0.1~seconds, a -factor of \emph{one hundred} faster. - -Before we continue, please pay attention to some caveats. -\begin{itemize} -\item The \hgext{inotify} extension is Linux-specific. Because it - interfaces directly to the Linux kernel's \texttt{inotify} - subsystem, it does not work on other operating systems. -\item It should work on any Linux distribution that was released after - early~2005. Older distributions are likely to have a kernel that - lacks \texttt{inotify}, or a version of \texttt{glibc} that does not - have the necessary interfacing support. -\item Not all filesystems are suitable for use with the - \hgext{inotify} extension. Network filesystems such as NFS are a - non-starter, for example, particularly if you're running Mercurial - on several systems, all mounting the same network filesystem. The - kernel's \texttt{inotify} system has no way of knowing about changes - made on another system. Most local filesystems (e.g.~ext3, XFS, - ReiserFS) should work fine. -\end{itemize} - -The \hgext{inotify} extension is not yet shipped with Mercurial as of -May~2007, so it's a little more involved to set up than other -extensions. But the performance improvement is worth it! - -The extension currently comes in two parts: a set of patches to the -Mercurial source code, and a library of Python bindings to the -\texttt{inotify} subsystem. -\begin{note} - There are \emph{two} Python \texttt{inotify} binding libraries. One - of them is called \texttt{pyinotify}, and is packaged by some Linux - distributions as \texttt{python-inotify}. This is \emph{not} the - one you'll need, as it is too buggy and inefficient to be practical. -\end{note} -To get going, it's best to already have a functioning copy of -Mercurial installed. -\begin{note} - If you follow the instructions below, you'll be \emph{replacing} and - overwriting any existing installation of Mercurial that you might - already have, using the latest ``bleeding edge'' Mercurial code. - Don't say you weren't warned! -\end{note} -\begin{enumerate} -\item Clone the Python \texttt{inotify} binding repository. Build and - install it. - \begin{codesample4} - hg clone http://hg.kublai.com/python/inotify - cd inotify - python setup.py build --force - sudo python setup.py install --skip-build - \end{codesample4} -\item Clone the \dirname{crew} Mercurial repository. Clone the - \hgext{inotify} patch repository so that Mercurial Queues will be - able to apply patches to your cope of the \dirname{crew} repository. - \begin{codesample4} - hg clone http://hg.intevation.org/mercurial/crew - hg clone crew inotify - hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches - \end{codesample4} -\item Make sure that you have the Mercurial Queues extension, - \hgext{mq}, enabled. If you've never used MQ, read - section~\ref{sec:mq:start} to get started quickly. -\item Go into the \dirname{inotify} repo, and apply all of the - \hgext{inotify} patches using the \hgxopt{mq}{qpush}{-a} option to - the \hgxcmd{mq}{qpush} command. - \begin{codesample4} - cd inotify - hg qpush -a - \end{codesample4} - If you get an error message from \hgxcmd{mq}{qpush}, you should not - continue. Instead, ask for help. -\item Build and install the patched version of Mercurial. - \begin{codesample4} - python setup.py build --force - sudo python setup.py install --skip-build - \end{codesample4} -\end{enumerate} -Once you've build a suitably patched version of Mercurial, all you -need to do to enable the \hgext{inotify} extension is add an entry to -your \hgrc. -\begin{codesample2} - [extensions] - inotify = -\end{codesample2} -When the \hgext{inotify} extension is enabled, Mercurial will -automatically and transparently start the status daemon the first time -you run a command that needs status in a repository. It runs one -status daemon per repository. - -The status daemon is started silently, and runs in the background. If -you look at a list of running processes after you've enabled the -\hgext{inotify} extension and run a few commands in different -repositories, you'll thus see a few \texttt{hg} processes sitting -around, waiting for updates from the kernel and queries from -Mercurial. - -The first time you run a Mercurial command in a repository when you -have the \hgext{inotify} extension enabled, it will run with about the -same performance as a normal Mercurial command. This is because the -status daemon needs to perform a normal status scan so that it has a -baseline against which to apply later updates from the kernel. -However, \emph{every} subsequent command that does any kind of status -check should be noticeably faster on repositories of even fairly -modest size. Better yet, the bigger your repository is, the greater a -performance advantage you'll see. The \hgext{inotify} daemon makes -status operations almost instantaneous on repositories of all sizes! - -If you like, you can manually start a status daemon using the -\hgxcmd{inotify}{inserve} command. This gives you slightly finer -control over how the daemon ought to run. This command will of course -only be available when the \hgext{inotify} extension is enabled. - -When you're using the \hgext{inotify} extension, you should notice -\emph{no difference at all} in Mercurial's behaviour, with the sole -exception of status-related commands running a whole lot faster than -they used to. You should specifically expect that commands will not -print different output; neither should they give different results. -If either of these situations occurs, please report a bug. - -\section{Flexible diff support with the \hgext{extdiff} extension} -\label{sec:hgext:extdiff} - -Mercurial's built-in \hgcmd{diff} command outputs plaintext unified -diffs. -\interaction{extdiff.diff} -If you would like to use an external tool to display modifications, -you'll want to use the \hgext{extdiff} extension. This will let you -use, for example, a graphical diff tool. - -The \hgext{extdiff} extension is bundled with Mercurial, so it's easy -to set up. In the \rcsection{extensions} section of your \hgrc, -simply add a one-line entry to enable the extension. -\begin{codesample2} - [extensions] - extdiff = -\end{codesample2} -This introduces a command named \hgxcmd{extdiff}{extdiff}, which by -default uses your system's \command{diff} command to generate a -unified diff in the same form as the built-in \hgcmd{diff} command. -\interaction{extdiff.extdiff} -The result won't be exactly the same as with the built-in \hgcmd{diff} -variations, because the output of \command{diff} varies from one -system to another, even when passed the same options. - -As the ``\texttt{making snapshot}'' lines of output above imply, the -\hgxcmd{extdiff}{extdiff} command works by creating two snapshots of -your source tree. The first snapshot is of the source revision; the -second, of the target revision or working directory. The -\hgxcmd{extdiff}{extdiff} command generates these snapshots in a -temporary directory, passes the name of each directory to an external -diff viewer, then deletes the temporary directory. For efficiency, it -only snapshots the directories and files that have changed between the -two revisions. - -Snapshot directory names have the same base name as your repository. -If your repository path is \dirname{/quux/bar/foo}, then \dirname{foo} -will be the name of each snapshot directory. Each snapshot directory -name has its changeset ID appended, if appropriate. If a snapshot is -of revision \texttt{a631aca1083f}, the directory will be named -\dirname{foo.a631aca1083f}. A snapshot of the working directory won't -have a changeset ID appended, so it would just be \dirname{foo} in -this example. To see what this looks like in practice, look again at -the \hgxcmd{extdiff}{extdiff} example above. Notice that the diff has -the snapshot directory names embedded in its header. - -The \hgxcmd{extdiff}{extdiff} command accepts two important options. -The \hgxopt{extdiff}{extdiff}{-p} option lets you choose a program to -view differences with, instead of \command{diff}. With the -\hgxopt{extdiff}{extdiff}{-o} option, you can change the options that -\hgxcmd{extdiff}{extdiff} passes to the program (by default, these -options are ``\texttt{-Npru}'', which only make sense if you're -running \command{diff}). In other respects, the -\hgxcmd{extdiff}{extdiff} command acts similarly to the built-in -\hgcmd{diff} command: you use the same option names, syntax, and -arguments to specify the revisions you want, the files you want, and -so on. - -As an example, here's how to run the normal system \command{diff} -command, getting it to generate context diffs (using the -\cmdopt{diff}{-c} option) instead of unified diffs, and five lines of -context instead of the default three (passing \texttt{5} as the -argument to the \cmdopt{diff}{-C} option). -\interaction{extdiff.extdiff-ctx} - -Launching a visual diff tool is just as easy. Here's how to launch -the \command{kdiff3} viewer. -\begin{codesample2} - hg extdiff -p kdiff3 -o '' -\end{codesample2} - -If your diff viewing command can't deal with directories, you can -easily work around this with a little scripting. For an example of -such scripting in action with the \hgext{mq} extension and the -\command{interdiff} command, see -section~\ref{mq-collab:tips:interdiff}. - -\subsection{Defining command aliases} - -It can be cumbersome to remember the options to both the -\hgxcmd{extdiff}{extdiff} command and the diff viewer you want to use, -so the \hgext{extdiff} extension lets you define \emph{new} commands -that will invoke your diff viewer with exactly the right options. - -All you need to do is edit your \hgrc, and add a section named -\rcsection{extdiff}. Inside this section, you can define multiple -commands. Here's how to add a \texttt{kdiff3} command. Once you've -defined this, you can type ``\texttt{hg kdiff3}'' and the -\hgext{extdiff} extension will run \command{kdiff3} for you. -\begin{codesample2} - [extdiff] - cmd.kdiff3 = -\end{codesample2} -If you leave the right hand side of the definition empty, as above, -the \hgext{extdiff} extension uses the name of the command you defined -as the name of the external program to run. But these names don't -have to be the same. Here, we define a command named ``\texttt{hg - wibble}'', which runs \command{kdiff3}. -\begin{codesample2} - [extdiff] - cmd.wibble = kdiff3 -\end{codesample2} - -You can also specify the default options that you want to invoke your -diff viewing program with. The prefix to use is ``\texttt{opts.}'', -followed by the name of the command to which the options apply. This -example defines a ``\texttt{hg vimdiff}'' command that runs the -\command{vim} editor's \texttt{DirDiff} extension. -\begin{codesample2} - [extdiff] - cmd.vimdiff = vim - opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' -\end{codesample2} - -\section{Cherrypicking changes with the \hgext{transplant} extension} -\label{sec:hgext:transplant} - -Need to have a long chat with Brendan about this. - -\section{Send changes via email with the \hgext{patchbomb} extension} -\label{sec:hgext:patchbomb} - -Many projects have a culture of ``change review'', in which people -send their modifications to a mailing list for others to read and -comment on before they commit the final version to a shared -repository. Some projects have people who act as gatekeepers; they -apply changes from other people to a repository to which those others -don't have access. - -Mercurial makes it easy to send changes over email for review or -application, via its \hgext{patchbomb} extension. The extension is so -namd because changes are formatted as patches, and it's usual to send -one changeset per email message. Sending a long series of changes by -email is thus much like ``bombing'' the recipient's inbox, hence -``patchbomb''. - -As usual, the basic configuration of the \hgext{patchbomb} extension -takes just one or two lines in your \hgrc. -\begin{codesample2} - [extensions] - patchbomb = -\end{codesample2} -Once you've enabled the extension, you will have a new command -available, named \hgxcmd{patchbomb}{email}. - -The safest and best way to invoke the \hgxcmd{patchbomb}{email} -command is to \emph{always} run it first with the -\hgxopt{patchbomb}{email}{-n} option. This will show you what the -command \emph{would} send, without actually sending anything. Once -you've had a quick glance over the changes and verified that you are -sending the right ones, you can rerun the same command, with the -\hgxopt{patchbomb}{email}{-n} option removed. - -The \hgxcmd{patchbomb}{email} command accepts the same kind of -revision syntax as every other Mercurial command. For example, this -command will send every revision between 7 and \texttt{tip}, -inclusive. -\begin{codesample2} - hg email -n 7:tip -\end{codesample2} -You can also specify a \emph{repository} to compare with. If you -provide a repository but no revisions, the \hgxcmd{patchbomb}{email} -command will send all revisions in the local repository that are not -present in the remote repository. If you additionally specify -revisions or a branch name (the latter using the -\hgxopt{patchbomb}{email}{-b} option), this will constrain the -revisions sent. - -It's perfectly safe to run the \hgxcmd{patchbomb}{email} command -without the names of the people you want to send to: if you do this, -it will just prompt you for those values interactively. (If you're -using a Linux or Unix-like system, you should have enhanced -\texttt{readline}-style editing capabilities when entering those -headers, too, which is useful.) - -When you are sending just one revision, the \hgxcmd{patchbomb}{email} -command will by default use the first line of the changeset -description as the subject of the single email message it sends. - -If you send multiple revisions, the \hgxcmd{patchbomb}{email} command -will usually send one message per changeset. It will preface the -series with an introductory message, in which you should describe the -purpose of the series of changes you're sending. - -\subsection{Changing the behaviour of patchbombs} - -Not every project has exactly the same conventions for sending changes -in email; the \hgext{patchbomb} extension tries to accommodate a -number of variations through command line options. -\begin{itemize} -\item You can write a subject for the introductory message on the - command line using the \hgxopt{patchbomb}{email}{-s} option. This - takes one argument, the text of the subject to use. -\item To change the email address from which the messages originate, - use the \hgxopt{patchbomb}{email}{-f} option. This takes one - argument, the email address to use. -\item The default behaviour is to send unified diffs (see - section~\ref{sec:mq:patch} for a description of the format), one per - message. You can send a binary bundle instead with the - \hgxopt{patchbomb}{email}{-b} option. -\item Unified diffs are normally prefaced with a metadata header. You - can omit this, and send unadorned diffs, with the - \hgxopt{patchbomb}{email}{--plain} option. -\item Diffs are normally sent ``inline'', in the same body part as the - description of a patch. This makes it easiest for the largest - number of readers to quote and respond to parts of a diff, as some - mail clients will only quote the first MIME body part in a message. - If you'd prefer to send the description and the diff in separate - body parts, use the \hgxopt{patchbomb}{email}{-a} option. -\item Instead of sending mail messages, you can write them to an - \texttt{mbox}-format mail folder using the - \hgxopt{patchbomb}{email}{-m} option. That option takes one - argument, the name of the file to write to. -\item If you would like to add a \command{diffstat}-format summary to - each patch, and one to the introductory message, use the - \hgxopt{patchbomb}{email}{-d} option. The \command{diffstat} - command displays a table containing the name of each file patched, - the number of lines affected, and a histogram showing how much each - file is modified. This gives readers a qualitative glance at how - complex a patch is. -\end{itemize} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/hook.tex --- a/fr/hook.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1413 +0,0 @@ -\chapter{Handling repository events with hooks} -\label{chap:hook} - -Mercurial offers a powerful mechanism to let you perform automated -actions in response to events that occur in a repository. In some -cases, you can even control Mercurial's response to those events. - -The name Mercurial uses for one of these actions is a \emph{hook}. -Hooks are called ``triggers'' in some revision control systems, but -the two names refer to the same idea. - -\section{An overview of hooks in Mercurial} - -Here is a brief list of the hooks that Mercurial supports. We will -revisit each of these hooks in more detail later, in -section~\ref{sec:hook:ref}. - -\begin{itemize} -\item[\small\hook{changegroup}] This is run after a group of - changesets has been brought into the repository from elsewhere. -\item[\small\hook{commit}] This is run after a new changeset has been - created in the local repository. -\item[\small\hook{incoming}] This is run once for each new changeset - that is brought into the repository from elsewhere. Notice the - difference from \hook{changegroup}, which is run once per - \emph{group} of changesets brought in. -\item[\small\hook{outgoing}] This is run after a group of changesets - has been transmitted from this repository. -\item[\small\hook{prechangegroup}] This is run before starting to - bring a group of changesets into the repository. -\item[\small\hook{precommit}] Controlling. This is run before starting - a commit. -\item[\small\hook{preoutgoing}] Controlling. This is run before - starting to transmit a group of changesets from this repository. -\item[\small\hook{pretag}] Controlling. This is run before creating a tag. -\item[\small\hook{pretxnchangegroup}] Controlling. This is run after a - group of changesets has been brought into the local repository from - another, but before the transaction completes that will make the - changes permanent in the repository. -\item[\small\hook{pretxncommit}] Controlling. This is run after a new - changeset has been created in the local repository, but before the - transaction completes that will make it permanent. -\item[\small\hook{preupdate}] Controlling. This is run before starting - an update or merge of the working directory. -\item[\small\hook{tag}] This is run after a tag is created. -\item[\small\hook{update}] This is run after an update or merge of the - working directory has finished. -\end{itemize} -Each of the hooks whose description begins with the word -``Controlling'' has the ability to determine whether an activity can -proceed. If the hook succeeds, the activity may proceed; if it fails, -the activity is either not permitted or undone, depending on the hook. - -\section{Hooks and security} - -\subsection{Hooks are run with your privileges} - -When you run a Mercurial command in a repository, and the command -causes a hook to run, that hook runs on \emph{your} system, under -\emph{your} user account, with \emph{your} privilege level. Since -hooks are arbitrary pieces of executable code, you should treat them -with an appropriate level of suspicion. Do not install a hook unless -you are confident that you know who created it and what it does. - -In some cases, you may be exposed to hooks that you did not install -yourself. If you work with Mercurial on an unfamiliar system, -Mercurial will run hooks defined in that system's global \hgrc\ file. - -If you are working with a repository owned by another user, Mercurial -can run hooks defined in that user's repository, but it will still run -them as ``you''. For example, if you \hgcmd{pull} from that -repository, and its \sfilename{.hg/hgrc} defines a local -\hook{outgoing} hook, that hook will run under your user account, even -though you don't own that repository. - -\begin{note} - This only applies if you are pulling from a repository on a local or - network filesystem. If you're pulling over http or ssh, any - \hook{outgoing} hook will run under whatever account is executing - the server process, on the server. -\end{note} - -XXX To see what hooks are defined in a repository, use the -\hgcmdargs{config}{hooks} command. If you are working in one -repository, but talking to another that you do not own (e.g.~using -\hgcmd{pull} or \hgcmd{incoming}), remember that it is the other -repository's hooks you should be checking, not your own. - -\subsection{Hooks do not propagate} - -In Mercurial, hooks are not revision controlled, and do not propagate -when you clone, or pull from, a repository. The reason for this is -simple: a hook is a completely arbitrary piece of executable code. It -runs under your user identity, with your privilege level, on your -machine. - -It would be extremely reckless for any distributed revision control -system to implement revision-controlled hooks, as this would offer an -easily exploitable way to subvert the accounts of users of the -revision control system. - -Since Mercurial does not propagate hooks, if you are collaborating -with other people on a common project, you should not assume that they -are using the same Mercurial hooks as you are, or that theirs are -correctly configured. You should document the hooks you expect people -to use. - -In a corporate intranet, this is somewhat easier to control, as you -can for example provide a ``standard'' installation of Mercurial on an -NFS filesystem, and use a site-wide \hgrc\ file to define hooks that -all users will see. However, this too has its limits; see below. - -\subsection{Hooks can be overridden} - -Mercurial allows you to override a hook definition by redefining the -hook. You can disable it by setting its value to the empty string, or -change its behaviour as you wish. - -If you deploy a system-~or site-wide \hgrc\ file that defines some -hooks, you should thus understand that your users can disable or -override those hooks. - -\subsection{Ensuring that critical hooks are run} - -Sometimes you may want to enforce a policy that you do not want others -to be able to work around. For example, you may have a requirement -that every changeset must pass a rigorous set of tests. Defining this -requirement via a hook in a site-wide \hgrc\ won't work for remote -users on laptops, and of course local users can subvert it at will by -overriding the hook. - -Instead, you can set up your policies for use of Mercurial so that -people are expected to propagate changes through a well-known -``canonical'' server that you have locked down and configured -appropriately. - -One way to do this is via a combination of social engineering and -technology. Set up a restricted-access account; users can push -changes over the network to repositories managed by this account, but -they cannot log into the account and run normal shell commands. In -this scenario, a user can commit a changeset that contains any old -garbage they want. - -When someone pushes a changeset to the server that everyone pulls -from, the server will test the changeset before it accepts it as -permanent, and reject it if it fails to pass the test suite. If -people only pull changes from this filtering server, it will serve to -ensure that all changes that people pull have been automatically -vetted. - -\section{Care with \texttt{pretxn} hooks in a shared-access repository} - -If you want to use hooks to do some automated work in a repository -that a number of people have shared access to, you need to be careful -in how you do this. - -Mercurial only locks a repository when it is writing to the -repository, and only the parts of Mercurial that write to the -repository pay attention to locks. Write locks are necessary to -prevent multiple simultaneous writers from scribbling on each other's -work, corrupting the repository. - -Because Mercurial is careful with the order in which it reads and -writes data, it does not need to acquire a lock when it wants to read -data from the repository. The parts of Mercurial that read from the -repository never pay attention to locks. This lockless reading scheme -greatly increases performance and concurrency. - -With great performance comes a trade-off, though, one which has the -potential to cause you trouble unless you're aware of it. To describe -this requires a little detail about how Mercurial adds changesets to a -repository and reads those changes. - -When Mercurial \emph{writes} metadata, it writes it straight into the -destination file. It writes file data first, then manifest data -(which contains pointers to the new file data), then changelog data -(which contains pointers to the new manifest data). Before the first -write to each file, it stores a record of where the end of the file -was in its transaction log. If the transaction must be rolled back, -Mercurial simply truncates each file back to the size it was before the -transaction began. - -When Mercurial \emph{reads} metadata, it reads the changelog first, -then everything else. Since a reader will only access parts of the -manifest or file metadata that it can see in the changelog, it can -never see partially written data. - -Some controlling hooks (\hook{pretxncommit} and -\hook{pretxnchangegroup}) run when a transaction is almost complete. -All of the metadata has been written, but Mercurial can still roll the -transaction back and cause the newly-written data to disappear. - -If one of these hooks runs for long, it opens a window of time during -which a reader can see the metadata for changesets that are not yet -permanent, and should not be thought of as ``really there''. The -longer the hook runs, the longer that window is open. - -\subsection{The problem illustrated} - -In principle, a good use for the \hook{pretxnchangegroup} hook would -be to automatically build and test incoming changes before they are -accepted into a central repository. This could let you guarantee that -nobody can push changes to this repository that ``break the build''. -But if a client can pull changes while they're being tested, the -usefulness of the test is zero; an unsuspecting someone can pull -untested changes, potentially breaking their build. - -The safest technological answer to this challenge is to set up such a -``gatekeeper'' repository as \emph{unidirectional}. Let it take -changes pushed in from the outside, but do not allow anyone to pull -changes from it (use the \hook{preoutgoing} hook to lock it down). -Configure a \hook{changegroup} hook so that if a build or test -succeeds, the hook will push the new changes out to another repository -that people \emph{can} pull from. - -In practice, putting a centralised bottleneck like this in place is -not often a good idea, and transaction visibility has nothing to do -with the problem. As the size of a project---and the time it takes to -build and test---grows, you rapidly run into a wall with this ``try -before you buy'' approach, where you have more changesets to test than -time in which to deal with them. The inevitable result is frustration -on the part of all involved. - -An approach that scales better is to get people to build and test -before they push, then run automated builds and tests centrally -\emph{after} a push, to be sure all is well. The advantage of this -approach is that it does not impose a limit on the rate at which the -repository can accept changes. - -\section{A short tutorial on using hooks} -\label{sec:hook:simple} - -It is easy to write a Mercurial hook. Let's start with a hook that -runs when you finish a \hgcmd{commit}, and simply prints the hash of -the changeset you just created. The hook is called \hook{commit}. - -\begin{figure}[ht] - \interaction{hook.simple.init} - \caption{A simple hook that runs when a changeset is committed} - \label{ex:hook:init} -\end{figure} - -All hooks follow the pattern in example~\ref{ex:hook:init}. You add -an entry to the \rcsection{hooks} section of your \hgrc. On the left -is the name of the event to trigger on; on the right is the action to -take. As you can see, you can run an arbitrary shell command in a -hook. Mercurial passes extra information to the hook using -environment variables (look for \envar{HG\_NODE} in the example). - -\subsection{Performing multiple actions per event} - -Quite often, you will want to define more than one hook for a -particular kind of event, as shown in example~\ref{ex:hook:ext}. -Mercurial lets you do this by adding an \emph{extension} to the end of -a hook's name. You extend a hook's name by giving the name of the -hook, followed by a full stop (the ``\texttt{.}'' character), followed -by some more text of your choosing. For example, Mercurial will run -both \texttt{commit.foo} and \texttt{commit.bar} when the -\texttt{commit} event occurs. - -\begin{figure}[ht] - \interaction{hook.simple.ext} - \caption{Defining a second \hook{commit} hook} - \label{ex:hook:ext} -\end{figure} - -To give a well-defined order of execution when there are multiple -hooks defined for an event, Mercurial sorts hooks by extension, and -executes the hook commands in this sorted order. In the above -example, it will execute \texttt{commit.bar} before -\texttt{commit.foo}, and \texttt{commit} before both. - -It is a good idea to use a somewhat descriptive extension when you -define a new hook. This will help you to remember what the hook was -for. If the hook fails, you'll get an error message that contains the -hook name and extension, so using a descriptive extension could give -you an immediate hint as to why the hook failed (see -section~\ref{sec:hook:perm} for an example). - -\subsection{Controlling whether an activity can proceed} -\label{sec:hook:perm} - -In our earlier examples, we used the \hook{commit} hook, which is -run after a commit has completed. This is one of several Mercurial -hooks that run after an activity finishes. Such hooks have no way of -influencing the activity itself. - -Mercurial defines a number of events that occur before an activity -starts; or after it starts, but before it finishes. Hooks that -trigger on these events have the added ability to choose whether the -activity can continue, or will abort. - -The \hook{pretxncommit} hook runs after a commit has all but -completed. In other words, the metadata representing the changeset -has been written out to disk, but the transaction has not yet been -allowed to complete. The \hook{pretxncommit} hook has the ability to -decide whether the transaction can complete, or must be rolled back. - -If the \hook{pretxncommit} hook exits with a status code of zero, the -transaction is allowed to complete; the commit finishes; and the -\hook{commit} hook is run. If the \hook{pretxncommit} hook exits with -a non-zero status code, the transaction is rolled back; the metadata -representing the changeset is erased; and the \hook{commit} hook is -not run. - -\begin{figure}[ht] - \interaction{hook.simple.pretxncommit} - \caption{Using the \hook{pretxncommit} hook to control commits} - \label{ex:hook:pretxncommit} -\end{figure} - -The hook in example~\ref{ex:hook:pretxncommit} checks that a commit -comment contains a bug ID. If it does, the commit can complete. If -not, the commit is rolled back. - -\section{Writing your own hooks} - -When you are writing a hook, you might find it useful to run Mercurial -either with the \hggopt{-v} option, or the \rcitem{ui}{verbose} config -item set to ``true''. When you do so, Mercurial will print a message -before it calls each hook. - -\subsection{Choosing how your hook should run} -\label{sec:hook:lang} - -You can write a hook either as a normal program---typically a shell -script---or as a Python function that is executed within the Mercurial -process. - -Writing a hook as an external program has the advantage that it -requires no knowledge of Mercurial's internals. You can call normal -Mercurial commands to get any added information you need. The -trade-off is that external hooks are slower than in-process hooks. - -An in-process Python hook has complete access to the Mercurial API, -and does not ``shell out'' to another process, so it is inherently -faster than an external hook. It is also easier to obtain much of the -information that a hook requires by using the Mercurial API than by -running Mercurial commands. - -If you are comfortable with Python, or require high performance, -writing your hooks in Python may be a good choice. However, when you -have a straightforward hook to write and you don't need to care about -performance (probably the majority of hooks), a shell script is -perfectly fine. - -\subsection{Hook parameters} -\label{sec:hook:param} - -Mercurial calls each hook with a set of well-defined parameters. In -Python, a parameter is passed as a keyword argument to your hook -function. For an external program, a parameter is passed as an -environment variable. - -Whether your hook is written in Python or as a shell script, the -hook-specific parameter names and values will be the same. A boolean -parameter will be represented as a boolean value in Python, but as the -number 1 (for ``true'') or 0 (for ``false'') as an environment -variable for an external hook. If a hook parameter is named -\texttt{foo}, the keyword argument for a Python hook will also be -named \texttt{foo}, while the environment variable for an external -hook will be named \texttt{HG\_FOO}. - -\subsection{Hook return values and activity control} - -A hook that executes successfully must exit with a status of zero if -external, or return boolean ``false'' if in-process. Failure is -indicated with a non-zero exit status from an external hook, or an -in-process hook returning boolean ``true''. If an in-process hook -raises an exception, the hook is considered to have failed. - -For a hook that controls whether an activity can proceed, zero/false -means ``allow'', while non-zero/true/exception means ``deny''. - -\subsection{Writing an external hook} - -When you define an external hook in your \hgrc\ and the hook is run, -its value is passed to your shell, which interprets it. This means -that you can use normal shell constructs in the body of the hook. - -An executable hook is always run with its current directory set to a -repository's root directory. - -Each hook parameter is passed in as an environment variable; the name -is upper-cased, and prefixed with the string ``\texttt{HG\_}''. - -With the exception of hook parameters, Mercurial does not set or -modify any environment variables when running a hook. This is useful -to remember if you are writing a site-wide hook that may be run by a -number of different users with differing environment variables set. -In multi-user situations, you should not rely on environment variables -being set to the values you have in your environment when testing the -hook. - -\subsection{Telling Mercurial to use an in-process hook} - -The \hgrc\ syntax for defining an in-process hook is slightly -different than for an executable hook. The value of the hook must -start with the text ``\texttt{python:}'', and continue with the -fully-qualified name of a callable object to use as the hook's value. - -The module in which a hook lives is automatically imported when a hook -is run. So long as you have the module name and \envar{PYTHONPATH} -right, it should ``just work''. - -The following \hgrc\ example snippet illustrates the syntax and -meaning of the notions we just described. -\begin{codesample2} - [hooks] - commit.example = python:mymodule.submodule.myhook -\end{codesample2} -When Mercurial runs the \texttt{commit.example} hook, it imports -\texttt{mymodule.submodule}, looks for the callable object named -\texttt{myhook}, and calls it. - -\subsection{Writing an in-process hook} - -The simplest in-process hook does nothing, but illustrates the basic -shape of the hook API: -\begin{codesample2} - def myhook(ui, repo, **kwargs): - pass -\end{codesample2} -The first argument to a Python hook is always a -\pymodclass{mercurial.ui}{ui} object. The second is a repository object; -at the moment, it is always an instance of -\pymodclass{mercurial.localrepo}{localrepository}. Following these two -arguments are other keyword arguments. Which ones are passed in -depends on the hook being called, but a hook can ignore arguments it -doesn't care about by dropping them into a keyword argument dict, as -with \texttt{**kwargs} above. - -\section{Some hook examples} - -\subsection{Writing meaningful commit messages} - -It's hard to imagine a useful commit message being very short. The -simple \hook{pretxncommit} hook of figure~\ref{ex:hook:msglen.go} -will prevent you from committing a changeset with a message that is -less than ten bytes long. - -\begin{figure}[ht] - \interaction{hook.msglen.go} - \caption{A hook that forbids overly short commit messages} - \label{ex:hook:msglen.go} -\end{figure} - -\subsection{Checking for trailing whitespace} - -An interesting use of a commit-related hook is to help you to write -cleaner code. A simple example of ``cleaner code'' is the dictum that -a change should not add any new lines of text that contain ``trailing -whitespace''. Trailing whitespace is a series of space and tab -characters at the end of a line of text. In most cases, trailing -whitespace is unnecessary, invisible noise, but it is occasionally -problematic, and people often prefer to get rid of it. - -You can use either the \hook{precommit} or \hook{pretxncommit} hook to -tell whether you have a trailing whitespace problem. If you use the -\hook{precommit} hook, the hook will not know which files you are -committing, so it will have to check every modified file in the -repository for trailing white space. If you want to commit a change -to just the file \filename{foo}, but the file \filename{bar} contains -trailing whitespace, doing a check in the \hook{precommit} hook will -prevent you from committing \filename{foo} due to the problem with -\filename{bar}. This doesn't seem right. - -Should you choose the \hook{pretxncommit} hook, the check won't occur -until just before the transaction for the commit completes. This will -allow you to check for problems only the exact files that are being -committed. However, if you entered the commit message interactively -and the hook fails, the transaction will roll back; you'll have to -re-enter the commit message after you fix the trailing whitespace and -run \hgcmd{commit} again. - -\begin{figure}[ht] - \interaction{hook.ws.simple} - \caption{A simple hook that checks for trailing whitespace} - \label{ex:hook:ws.simple} -\end{figure} - -Figure~\ref{ex:hook:ws.simple} introduces a simple \hook{pretxncommit} -hook that checks for trailing whitespace. This hook is short, but not -very helpful. It exits with an error status if a change adds a line -with trailing whitespace to any file, but does not print any -information that might help us to identify the offending file or -line. It also has the nice property of not paying attention to -unmodified lines; only lines that introduce new trailing whitespace -cause problems. - -\begin{figure}[ht] - \interaction{hook.ws.better} - \caption{A better trailing whitespace hook} - \label{ex:hook:ws.better} -\end{figure} - -The example of figure~\ref{ex:hook:ws.better} is much more complex, -but also more useful. It parses a unified diff to see if any lines -add trailing whitespace, and prints the name of the file and the line -number of each such occurrence. Even better, if the change adds -trailing whitespace, this hook saves the commit comment and prints the -name of the save file before exiting and telling Mercurial to roll the -transaction back, so you can use -\hgcmdargs{commit}{\hgopt{commit}{-l}~\emph{filename}} to reuse the -saved commit message once you've corrected the problem. - -As a final aside, note in figure~\ref{ex:hook:ws.better} the use of -\command{perl}'s in-place editing feature to get rid of trailing -whitespace from a file. This is concise and useful enough that I will -reproduce it here. -\begin{codesample2} - perl -pi -e 's,\textbackslash{}s+\$,,' filename -\end{codesample2} - -\section{Bundled hooks} - -Mercurial ships with several bundled hooks. You can find them in the -\dirname{hgext} directory of a Mercurial source tree. If you are -using a Mercurial binary package, the hooks will be located in the -\dirname{hgext} directory of wherever your package installer put -Mercurial. - -\subsection{\hgext{acl}---access control for parts of a repository} - -The \hgext{acl} extension lets you control which remote users are -allowed to push changesets to a networked server. You can protect any -portion of a repository (including the entire repo), so that a -specific remote user can push changes that do not affect the protected -portion. - -This extension implements access control based on the identity of the -user performing a push, \emph{not} on who committed the changesets -they're pushing. It makes sense to use this hook only if you have a -locked-down server environment that authenticates remote users, and -you want to be sure that only specific users are allowed to push -changes to that server. - -\subsubsection{Configuring the \hook{acl} hook} - -In order to manage incoming changesets, the \hgext{acl} hook must be -used as a \hook{pretxnchangegroup} hook. This lets it see which files -are modified by each incoming changeset, and roll back a group of -changesets if they modify ``forbidden'' files. Example: -\begin{codesample2} - [hooks] - pretxnchangegroup.acl = python:hgext.acl.hook -\end{codesample2} - -The \hgext{acl} extension is configured using three sections. - -The \rcsection{acl} section has only one entry, \rcitem{acl}{sources}, -which lists the sources of incoming changesets that the hook should -pay attention to. You don't normally need to configure this section. -\begin{itemize} -\item[\rcitem{acl}{serve}] Control incoming changesets that are arriving - from a remote repository over http or ssh. This is the default - value of \rcitem{acl}{sources}, and usually the only setting you'll - need for this configuration item. -\item[\rcitem{acl}{pull}] Control incoming changesets that are - arriving via a pull from a local repository. -\item[\rcitem{acl}{push}] Control incoming changesets that are - arriving via a push from a local repository. -\item[\rcitem{acl}{bundle}] Control incoming changesets that are - arriving from another repository via a bundle. -\end{itemize} - -The \rcsection{acl.allow} section controls the users that are allowed to -add changesets to the repository. If this section is not present, all -users that are not explicitly denied are allowed. If this section is -present, all users that are not explicitly allowed are denied (so an -empty section means that all users are denied). - -The \rcsection{acl.deny} section determines which users are denied -from adding changesets to the repository. If this section is not -present or is empty, no users are denied. - -The syntaxes for the \rcsection{acl.allow} and \rcsection{acl.deny} -sections are identical. On the left of each entry is a glob pattern -that matches files or directories, relative to the root of the -repository; on the right, a user name. - -In the following example, the user \texttt{docwriter} can only push -changes to the \dirname{docs} subtree of the repository, while -\texttt{intern} can push changes to any file or directory except -\dirname{source/sensitive}. -\begin{codesample2} - [acl.allow] - docs/** = docwriter - - [acl.deny] - source/sensitive/** = intern -\end{codesample2} - -\subsubsection{Testing and troubleshooting} - -If you want to test the \hgext{acl} hook, run it with Mercurial's -debugging output enabled. Since you'll probably be running it on a -server where it's not convenient (or sometimes possible) to pass in -the \hggopt{--debug} option, don't forget that you can enable -debugging output in your \hgrc: -\begin{codesample2} - [ui] - debug = true -\end{codesample2} -With this enabled, the \hgext{acl} hook will print enough information -to let you figure out why it is allowing or forbidding pushes from -specific users. - -\subsection{\hgext{bugzilla}---integration with Bugzilla} - -The \hgext{bugzilla} extension adds a comment to a Bugzilla bug -whenever it finds a reference to that bug ID in a commit comment. You -can install this hook on a shared server, so that any time a remote -user pushes changes to this server, the hook gets run. - -It adds a comment to the bug that looks like this (you can configure -the contents of the comment---see below): -\begin{codesample2} - Changeset aad8b264143a, made by Joe User in - the frobnitz repository, refers to this bug. - - For complete details, see - http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a - - Changeset description: - Fix bug 10483 by guarding against some NULL pointers -\end{codesample2} -The value of this hook is that it automates the process of updating a -bug any time a changeset refers to it. If you configure the hook -properly, it makes it easy for people to browse straight from a -Bugzilla bug to a changeset that refers to that bug. - -You can use the code in this hook as a starting point for some more -exotic Bugzilla integration recipes. Here are a few possibilities: -\begin{itemize} -\item Require that every changeset pushed to the server have a valid - bug~ID in its commit comment. In this case, you'd want to configure - the hook as a \hook{pretxncommit} hook. This would allow the hook - to reject changes that didn't contain bug IDs. -\item Allow incoming changesets to automatically modify the - \emph{state} of a bug, as well as simply adding a comment. For - example, the hook could recognise the string ``fixed bug 31337'' as - indicating that it should update the state of bug 31337 to - ``requires testing''. -\end{itemize} - -\subsubsection{Configuring the \hook{bugzilla} hook} -\label{sec:hook:bugzilla:config} - -You should configure this hook in your server's \hgrc\ as an -\hook{incoming} hook, for example as follows: -\begin{codesample2} - [hooks] - incoming.bugzilla = python:hgext.bugzilla.hook -\end{codesample2} - -Because of the specialised nature of this hook, and because Bugzilla -was not written with this kind of integration in mind, configuring -this hook is a somewhat involved process. - -Before you begin, you must install the MySQL bindings for Python on -the host(s) where you'll be running the hook. If this is not -available as a binary package for your system, you can download it -from~\cite{web:mysql-python}. - -Configuration information for this hook lives in the -\rcsection{bugzilla} section of your \hgrc. -\begin{itemize} -\item[\rcitem{bugzilla}{version}] The version of Bugzilla installed on - the server. The database schema that Bugzilla uses changes - occasionally, so this hook has to know exactly which schema to use. - At the moment, the only version supported is \texttt{2.16}. -\item[\rcitem{bugzilla}{host}] The hostname of the MySQL server that - stores your Bugzilla data. The database must be configured to allow - connections from whatever host you are running the \hook{bugzilla} - hook on. -\item[\rcitem{bugzilla}{user}] The username with which to connect to - the MySQL server. The database must be configured to allow this - user to connect from whatever host you are running the - \hook{bugzilla} hook on. This user must be able to access and - modify Bugzilla tables. The default value of this item is - \texttt{bugs}, which is the standard name of the Bugzilla user in a - MySQL database. -\item[\rcitem{bugzilla}{password}] The MySQL password for the user you - configured above. This is stored as plain text, so you should make - sure that unauthorised users cannot read the \hgrc\ file where you - store this information. -\item[\rcitem{bugzilla}{db}] The name of the Bugzilla database on the - MySQL server. The default value of this item is \texttt{bugs}, - which is the standard name of the MySQL database where Bugzilla - stores its data. -\item[\rcitem{bugzilla}{notify}] If you want Bugzilla to send out a - notification email to subscribers after this hook has added a - comment to a bug, you will need this hook to run a command whenever - it updates the database. The command to run depends on where you - have installed Bugzilla, but it will typically look something like - this, if you have Bugzilla installed in - \dirname{/var/www/html/bugzilla}: - \begin{codesample4} - cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com - \end{codesample4} - The Bugzilla \texttt{processmail} program expects to be given a - bug~ID (the hook replaces ``\texttt{\%s}'' with the bug~ID) and an - email address. It also expects to be able to write to some files in - the directory that it runs in. If Bugzilla and this hook are not - installed on the same machine, you will need to find a way to run - \texttt{processmail} on the server where Bugzilla is installed. -\end{itemize} - -\subsubsection{Mapping committer names to Bugzilla user names} - -By default, the \hgext{bugzilla} hook tries to use the email address -of a changeset's committer as the Bugzilla user name with which to -update a bug. If this does not suit your needs, you can map committer -email addresses to Bugzilla user names using a \rcsection{usermap} -section. - -Each item in the \rcsection{usermap} section contains an email address -on the left, and a Bugzilla user name on the right. -\begin{codesample2} - [usermap] - jane.user@example.com = jane -\end{codesample2} -You can either keep the \rcsection{usermap} data in a normal \hgrc, or -tell the \hgext{bugzilla} hook to read the information from an -external \filename{usermap} file. In the latter case, you can store -\filename{usermap} data by itself in (for example) a user-modifiable -repository. This makes it possible to let your users maintain their -own \rcitem{bugzilla}{usermap} entries. The main \hgrc\ file might -look like this: -\begin{codesample2} - # regular hgrc file refers to external usermap file - [bugzilla] - usermap = /home/hg/repos/userdata/bugzilla-usermap.conf -\end{codesample2} -While the \filename{usermap} file that it refers to might look like -this: -\begin{codesample2} - # bugzilla-usermap.conf - inside a hg repository - [usermap] - stephanie@example.com = steph -\end{codesample2} - -\subsubsection{Configuring the text that gets added to a bug} - -You can configure the text that this hook adds as a comment; you -specify it in the form of a Mercurial template. Several \hgrc\ -entries (still in the \rcsection{bugzilla} section) control this -behaviour. -\begin{itemize} -\item[\texttt{strip}] The number of leading path elements to strip - from a repository's path name to construct a partial path for a URL. - For example, if the repositories on your server live under - \dirname{/home/hg/repos}, and you have a repository whose path is - \dirname{/home/hg/repos/app/tests}, then setting \texttt{strip} to - \texttt{4} will give a partial path of \dirname{app/tests}. The - hook will make this partial path available when expanding a - template, as \texttt{webroot}. -\item[\texttt{template}] The text of the template to use. In addition - to the usual changeset-related variables, this template can use - \texttt{hgweb} (the value of the \texttt{hgweb} configuration item - above) and \texttt{webroot} (the path constructed using - \texttt{strip} above). -\end{itemize} - -In addition, you can add a \rcitem{web}{baseurl} item to the -\rcsection{web} section of your \hgrc. The \hgext{bugzilla} hook will -make this available when expanding a template, as the base string to -use when constructing a URL that will let users browse from a Bugzilla -comment to view a changeset. Example: -\begin{codesample2} - [web] - baseurl = http://hg.domain.com/ -\end{codesample2} - -Here is an example set of \hgext{bugzilla} hook config information. -\begin{codesample2} - [bugzilla] - host = bugzilla.example.com - password = mypassword - version = 2.16 - # server-side repos live in /home/hg/repos, so strip 4 leading - # separators - strip = 4 - hgweb = http://hg.example.com/ - usermap = /home/hg/repos/notify/bugzilla.conf - template = Changeset \{node|short\}, made by \{author\} in the \{webroot\} - repo, refers to this bug.\\nFor complete details, see - \{hgweb\}\{webroot\}?cmd=changeset;node=\{node|short\}\\nChangeset - description:\\n\\t\{desc|tabindent\} -\end{codesample2} - -\subsubsection{Testing and troubleshooting} - -The most common problems with configuring the \hgext{bugzilla} hook -relate to running Bugzilla's \filename{processmail} script and mapping -committer names to user names. - -Recall from section~\ref{sec:hook:bugzilla:config} above that the user -that runs the Mercurial process on the server is also the one that -will run the \filename{processmail} script. The -\filename{processmail} script sometimes causes Bugzilla to write to -files in its configuration directory, and Bugzilla's configuration -files are usually owned by the user that your web server runs under. - -You can cause \filename{processmail} to be run with the suitable -user's identity using the \command{sudo} command. Here is an example -entry for a \filename{sudoers} file. -\begin{codesample2} - hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s -\end{codesample2} -This allows the \texttt{hg\_user} user to run a -\filename{processmail-wrapper} program under the identity of -\texttt{httpd\_user}. - -This indirection through a wrapper script is necessary, because -\filename{processmail} expects to be run with its current directory -set to wherever you installed Bugzilla; you can't specify that kind of -constraint in a \filename{sudoers} file. The contents of the wrapper -script are simple: -\begin{codesample2} - #!/bin/sh - cd `dirname $0` && ./processmail "$1" nobody@example.com -\end{codesample2} -It doesn't seem to matter what email address you pass to -\filename{processmail}. - -If your \rcsection{usermap} is not set up correctly, users will see an -error message from the \hgext{bugzilla} hook when they push changes -to the server. The error message will look like this: -\begin{codesample2} - cannot find bugzilla user id for john.q.public@example.com -\end{codesample2} -What this means is that the committer's address, -\texttt{john.q.public@example.com}, is not a valid Bugzilla user name, -nor does it have an entry in your \rcsection{usermap} that maps it to -a valid Bugzilla user name. - -\subsection{\hgext{notify}---send email notifications} - -Although Mercurial's built-in web server provides RSS feeds of changes -in every repository, many people prefer to receive change -notifications via email. The \hgext{notify} hook lets you send out -notifications to a set of email addresses whenever changesets arrive -that those subscribers are interested in. - -As with the \hgext{bugzilla} hook, the \hgext{notify} hook is -template-driven, so you can customise the contents of the notification -messages that it sends. - -By default, the \hgext{notify} hook includes a diff of every changeset -that it sends out; you can limit the size of the diff, or turn this -feature off entirely. It is useful for letting subscribers review -changes immediately, rather than clicking to follow a URL. - -\subsubsection{Configuring the \hgext{notify} hook} - -You can set up the \hgext{notify} hook to send one email message per -incoming changeset, or one per incoming group of changesets (all those -that arrived in a single pull or push). -\begin{codesample2} - [hooks] - # send one email per group of changes - changegroup.notify = python:hgext.notify.hook - # send one email per change - incoming.notify = python:hgext.notify.hook -\end{codesample2} - -Configuration information for this hook lives in the -\rcsection{notify} section of a \hgrc\ file. -\begin{itemize} -\item[\rcitem{notify}{test}] By default, this hook does not send out - email at all; instead, it prints the message that it \emph{would} - send. Set this item to \texttt{false} to allow email to be sent. - The reason that sending of email is turned off by default is that it - takes several tries to configure this extension exactly as you would - like, and it would be bad form to spam subscribers with a number of - ``broken'' notifications while you debug your configuration. -\item[\rcitem{notify}{config}] The path to a configuration file that - contains subscription information. This is kept separate from the - main \hgrc\ so that you can maintain it in a repository of its own. - People can then clone that repository, update their subscriptions, - and push the changes back to your server. -\item[\rcitem{notify}{strip}] The number of leading path separator - characters to strip from a repository's path, when deciding whether - a repository has subscribers. For example, if the repositories on - your server live in \dirname{/home/hg/repos}, and \hgext{notify} is - considering a repository named \dirname{/home/hg/repos/shared/test}, - setting \rcitem{notify}{strip} to \texttt{4} will cause - \hgext{notify} to trim the path it considers down to - \dirname{shared/test}, and it will match subscribers against that. -\item[\rcitem{notify}{template}] The template text to use when sending - messages. This specifies both the contents of the message header - and its body. -\item[\rcitem{notify}{maxdiff}] The maximum number of lines of diff - data to append to the end of a message. If a diff is longer than - this, it is truncated. By default, this is set to 300. Set this to - \texttt{0} to omit diffs from notification emails. -\item[\rcitem{notify}{sources}] A list of sources of changesets to - consider. This lets you limit \hgext{notify} to only sending out - email about changes that remote users pushed into this repository - via a server, for example. See section~\ref{sec:hook:sources} for - the sources you can specify here. -\end{itemize} - -If you set the \rcitem{web}{baseurl} item in the \rcsection{web} -section, you can use it in a template; it will be available as -\texttt{webroot}. - -Here is an example set of \hgext{notify} configuration information. -\begin{codesample2} - [notify] - # really send email - test = false - # subscriber data lives in the notify repo - config = /home/hg/repos/notify/notify.conf - # repos live in /home/hg/repos on server, so strip 4 "/" chars - strip = 4 - template = X-Hg-Repo: \{webroot\} - Subject: \{webroot\}: \{desc|firstline|strip\} - From: \{author\} - - changeset \{node|short\} in \{root\} - details: \{baseurl\}\{webroot\}?cmd=changeset;node=\{node|short\} - description: - \{desc|tabindent|strip\} - - [web] - baseurl = http://hg.example.com/ -\end{codesample2} - -This will produce a message that looks like the following: -\begin{codesample2} - X-Hg-Repo: tests/slave - Subject: tests/slave: Handle error case when slave has no buffers - Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) - - changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave - details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 - description: - Handle error case when slave has no buffers - diffs (54 lines): - - diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h - --- a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 - +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 - @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h) - [...snip...] -\end{codesample2} - -\subsubsection{Testing and troubleshooting} - -Do not forget that by default, the \hgext{notify} extension \emph{will - not send any mail} until you explicitly configure it to do so, by -setting \rcitem{notify}{test} to \texttt{false}. Until you do that, -it simply prints the message it \emph{would} send. - -\section{Information for writers of hooks} -\label{sec:hook:ref} - -\subsection{In-process hook execution} - -An in-process hook is called with arguments of the following form: -\begin{codesample2} - def myhook(ui, repo, **kwargs): - pass -\end{codesample2} -The \texttt{ui} parameter is a \pymodclass{mercurial.ui}{ui} object. -The \texttt{repo} parameter is a -\pymodclass{mercurial.localrepo}{localrepository} object. The -names and values of the \texttt{**kwargs} parameters depend on the -hook being invoked, with the following common features: -\begin{itemize} -\item If a parameter is named \texttt{node} or - \texttt{parent\emph{N}}, it will contain a hexadecimal changeset ID. - The empty string is used to represent ``null changeset ID'' instead - of a string of zeroes. -\item If a parameter is named \texttt{url}, it will contain the URL of - a remote repository, if that can be determined. -\item Boolean-valued parameters are represented as Python - \texttt{bool} objects. -\end{itemize} - -An in-process hook is called without a change to the process's working -directory (unlike external hooks, which are run in the root of the -repository). It must not change the process's working directory, or -it will cause any calls it makes into the Mercurial API to fail. - -If a hook returns a boolean ``false'' value, it is considered to have -succeeded. If it returns a boolean ``true'' value or raises an -exception, it is considered to have failed. A useful way to think of -the calling convention is ``tell me if you fail''. - -Note that changeset IDs are passed into Python hooks as hexadecimal -strings, not the binary hashes that Mercurial's APIs normally use. To -convert a hash from hex to binary, use the -\pymodfunc{mercurial.node}{bin} function. - -\subsection{External hook execution} - -An external hook is passed to the shell of the user running Mercurial. -Features of that shell, such as variable substitution and command -redirection, are available. The hook is run in the root directory of -the repository (unlike in-process hooks, which are run in the same -directory that Mercurial was run in). - -Hook parameters are passed to the hook as environment variables. Each -environment variable's name is converted in upper case and prefixed -with the string ``\texttt{HG\_}''. For example, if the name of a -parameter is ``\texttt{node}'', the name of the environment variable -representing that parameter will be ``\texttt{HG\_NODE}''. - -A boolean parameter is represented as the string ``\texttt{1}'' for -``true'', ``\texttt{0}'' for ``false''. If an environment variable is -named \envar{HG\_NODE}, \envar{HG\_PARENT1} or \envar{HG\_PARENT2}, it -contains a changeset ID represented as a hexadecimal string. The -empty string is used to represent ``null changeset ID'' instead of a -string of zeroes. If an environment variable is named -\envar{HG\_URL}, it will contain the URL of a remote repository, if -that can be determined. - -If a hook exits with a status of zero, it is considered to have -succeeded. If it exits with a non-zero status, it is considered to -have failed. - -\subsection{Finding out where changesets come from} - -A hook that involves the transfer of changesets between a local -repository and another may be able to find out information about the -``far side''. Mercurial knows \emph{how} changes are being -transferred, and in many cases \emph{where} they are being transferred -to or from. - -\subsubsection{Sources of changesets} -\label{sec:hook:sources} - -Mercurial will tell a hook what means are, or were, used to transfer -changesets between repositories. This is provided by Mercurial in a -Python parameter named \texttt{source}, or an environment variable named -\envar{HG\_SOURCE}. - -\begin{itemize} -\item[\texttt{serve}] Changesets are transferred to or from a remote - repository over http or ssh. -\item[\texttt{pull}] Changesets are being transferred via a pull from - one repository into another. -\item[\texttt{push}] Changesets are being transferred via a push from - one repository into another. -\item[\texttt{bundle}] Changesets are being transferred to or from a - bundle. -\end{itemize} - -\subsubsection{Where changes are going---remote repository URLs} -\label{sec:hook:url} - -When possible, Mercurial will tell a hook the location of the ``far -side'' of an activity that transfers changeset data between -repositories. This is provided by Mercurial in a Python parameter -named \texttt{url}, or an environment variable named \envar{HG\_URL}. - -This information is not always known. If a hook is invoked in a -repository that is being served via http or ssh, Mercurial cannot tell -where the remote repository is, but it may know where the client is -connecting from. In such cases, the URL will take one of the -following forms: -\begin{itemize} -\item \texttt{remote:ssh:\emph{ip-address}}---remote ssh client, at - the given IP address. -\item \texttt{remote:http:\emph{ip-address}}---remote http client, at - the given IP address. If the client is using SSL, this will be of - the form \texttt{remote:https:\emph{ip-address}}. -\item Empty---no information could be discovered about the remote - client. -\end{itemize} - -\section{Hook reference} - -\subsection{\hook{changegroup}---after remote changesets added} -\label{sec:hook:changegroup} - -This hook is run after a group of pre-existing changesets has been -added to the repository, for example via a \hgcmd{pull} or -\hgcmd{unbundle}. This hook is run once per operation that added one -or more changesets. This is in contrast to the \hook{incoming} hook, -which is run once per changeset, regardless of whether the changesets -arrive in a group. - -Some possible uses for this hook include kicking off an automated -build or test of the added changesets, updating a bug database, or -notifying subscribers that a repository contains new changes. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The changeset ID of the first - changeset in the group that was added. All changesets between this - and \index{tags!\texttt{tip}}\texttt{tip}, inclusive, were added by - a single \hgcmd{pull}, \hgcmd{push} or \hgcmd{unbundle}. -\item[\texttt{source}] A string. The source of these changes. See - section~\ref{sec:hook:sources} for details. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{incoming} (section~\ref{sec:hook:incoming}), -\hook{prechangegroup} (section~\ref{sec:hook:prechangegroup}), -\hook{pretxnchangegroup} (section~\ref{sec:hook:pretxnchangegroup}) - -\subsection{\hook{commit}---after a new changeset is created} -\label{sec:hook:commit} - -This hook is run after a new changeset has been created. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The changeset ID of the newly - committed changeset. -\item[\texttt{parent1}] A changeset ID. The changeset ID of the first - parent of the newly committed changeset. -\item[\texttt{parent2}] A changeset ID. The changeset ID of the second - parent of the newly committed changeset. -\end{itemize} - -See also: \hook{precommit} (section~\ref{sec:hook:precommit}), -\hook{pretxncommit} (section~\ref{sec:hook:pretxncommit}) - -\subsection{\hook{incoming}---after one remote changeset is added} -\label{sec:hook:incoming} - -This hook is run after a pre-existing changeset has been added to the -repository, for example via a \hgcmd{push}. If a group of changesets -was added in a single operation, this hook is called once for each -added changeset. - -You can use this hook for the same purposes as the \hook{changegroup} -hook (section~\ref{sec:hook:changegroup}); it's simply more convenient -sometimes to run a hook once per group of changesets, while other -times it's handier once per changeset. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The ID of the newly added - changeset. -\item[\texttt{source}] A string. The source of these changes. See - section~\ref{sec:hook:sources} for details. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{changegroup} (section~\ref{sec:hook:changegroup}) \hook{prechangegroup} (section~\ref{sec:hook:prechangegroup}), \hook{pretxnchangegroup} (section~\ref{sec:hook:pretxnchangegroup}) - -\subsection{\hook{outgoing}---after changesets are propagated} -\label{sec:hook:outgoing} - -This hook is run after a group of changesets has been propagated out -of this repository, for example by a \hgcmd{push} or \hgcmd{bundle} -command. - -One possible use for this hook is to notify administrators that -changes have been pulled. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The changeset ID of the first - changeset of the group that was sent. -\item[\texttt{source}] A string. The source of the of the operation - (see section~\ref{sec:hook:sources}). If a remote client pulled - changes from this repository, \texttt{source} will be - \texttt{serve}. If the client that obtained changes from this - repository was local, \texttt{source} will be \texttt{bundle}, - \texttt{pull}, or \texttt{push}, depending on the operation the - client performed. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{preoutgoing} (section~\ref{sec:hook:preoutgoing}) - -\subsection{\hook{prechangegroup}---before starting to add remote changesets} -\label{sec:hook:prechangegroup} - -This controlling hook is run before Mercurial begins to add a group of -changesets from another repository. - -This hook does not have any information about the changesets to be -added, because it is run before transmission of those changesets is -allowed to begin. If this hook fails, the changesets will not be -transmitted. - -One use for this hook is to prevent external changes from being added -to a repository. For example, you could use this to ``freeze'' a -server-hosted branch temporarily or permanently so that users cannot -push to it, while still allowing a local administrator to modify the -repository. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{source}] A string. The source of these changes. See - section~\ref{sec:hook:sources} for details. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{changegroup} (section~\ref{sec:hook:changegroup}), -\hook{incoming} (section~\ref{sec:hook:incoming}), , -\hook{pretxnchangegroup} (section~\ref{sec:hook:pretxnchangegroup}) - -\subsection{\hook{precommit}---before starting to commit a changeset} -\label{sec:hook:precommit} - -This hook is run before Mercurial begins to commit a new changeset. -It is run before Mercurial has any of the metadata for the commit, -such as the files to be committed, the commit message, or the commit -date. - -One use for this hook is to disable the ability to commit new -changesets, while still allowing incoming changesets. Another is to -run a build or test, and only allow the commit to begin if the build -or test succeeds. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{parent1}] A changeset ID. The changeset ID of the first - parent of the working directory. -\item[\texttt{parent2}] A changeset ID. The changeset ID of the second - parent of the working directory. -\end{itemize} -If the commit proceeds, the parents of the working directory will -become the parents of the new changeset. - -See also: \hook{commit} (section~\ref{sec:hook:commit}), -\hook{pretxncommit} (section~\ref{sec:hook:pretxncommit}) - -\subsection{\hook{preoutgoing}---before starting to propagate changesets} -\label{sec:hook:preoutgoing} - -This hook is invoked before Mercurial knows the identities of the -changesets to be transmitted. - -One use for this hook is to prevent changes from being transmitted to -another repository. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{source}] A string. The source of the operation that is - attempting to obtain changes from this repository (see - section~\ref{sec:hook:sources}). See the documentation for the - \texttt{source} parameter to the \hook{outgoing} hook, in - section~\ref{sec:hook:outgoing}, for possible values of this - parameter. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{outgoing} (section~\ref{sec:hook:outgoing}) - -\subsection{\hook{pretag}---before tagging a changeset} -\label{sec:hook:pretag} - -This controlling hook is run before a tag is created. If the hook -succeeds, creation of the tag proceeds. If the hook fails, the tag is -not created. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{local}] A boolean. Whether the tag is local to this - repository instance (i.e.~stored in \sfilename{.hg/localtags}) or - managed by Mercurial (stored in \sfilename{.hgtags}). -\item[\texttt{node}] A changeset ID. The ID of the changeset to be tagged. -\item[\texttt{tag}] A string. The name of the tag to be created. -\end{itemize} - -If the tag to be created is revision-controlled, the \hook{precommit} -and \hook{pretxncommit} hooks (sections~\ref{sec:hook:commit} -and~\ref{sec:hook:pretxncommit}) will also be run. - -See also: \hook{tag} (section~\ref{sec:hook:tag}) - -\subsection{\hook{pretxnchangegroup}---before completing addition of - remote changesets} -\label{sec:hook:pretxnchangegroup} - -This controlling hook is run before a transaction---that manages the -addition of a group of new changesets from outside the -repository---completes. If the hook succeeds, the transaction -completes, and all of the changesets become permanent within this -repository. If the hook fails, the transaction is rolled back, and -the data for the changesets is erased. - -This hook can access the metadata associated with the almost-added -changesets, but it should not do anything permanent with this data. -It must also not modify the working directory. - -While this hook is running, if other Mercurial processes access this -repository, they will be able to see the almost-added changesets as if -they are permanent. This may lead to race conditions if you do not -take steps to avoid them. - -This hook can be used to automatically vet a group of changesets. If -the hook fails, all of the changesets are ``rejected'' when the -transaction rolls back. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The changeset ID of the first - changeset in the group that was added. All changesets between this - and \index{tags!\texttt{tip}}\texttt{tip}, inclusive, were added by - a single \hgcmd{pull}, \hgcmd{push} or \hgcmd{unbundle}. -\item[\texttt{source}] A string. The source of these changes. See - section~\ref{sec:hook:sources} for details. -\item[\texttt{url}] A URL. The location of the remote repository, if - known. See section~\ref{sec:hook:url} for more information. -\end{itemize} - -See also: \hook{changegroup} (section~\ref{sec:hook:changegroup}), -\hook{incoming} (section~\ref{sec:hook:incoming}), -\hook{prechangegroup} (section~\ref{sec:hook:prechangegroup}) - -\subsection{\hook{pretxncommit}---before completing commit of new changeset} -\label{sec:hook:pretxncommit} - -This controlling hook is run before a transaction---that manages a new -commit---completes. If the hook succeeds, the transaction completes -and the changeset becomes permanent within this repository. If the -hook fails, the transaction is rolled back, and the commit data is -erased. - -This hook can access the metadata associated with the almost-new -changeset, but it should not do anything permanent with this data. It -must also not modify the working directory. - -While this hook is running, if other Mercurial processes access this -repository, they will be able to see the almost-new changeset as if it -is permanent. This may lead to race conditions if you do not take -steps to avoid them. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{node}] A changeset ID. The changeset ID of the newly - committed changeset. -\item[\texttt{parent1}] A changeset ID. The changeset ID of the first - parent of the newly committed changeset. -\item[\texttt{parent2}] A changeset ID. The changeset ID of the second - parent of the newly committed changeset. -\end{itemize} - -See also: \hook{precommit} (section~\ref{sec:hook:precommit}) - -\subsection{\hook{preupdate}---before updating or merging working directory} -\label{sec:hook:preupdate} - -This controlling hook is run before an update or merge of the working -directory begins. It is run only if Mercurial's normal pre-update -checks determine that the update or merge can proceed. If the hook -succeeds, the update or merge may proceed; if it fails, the update or -merge does not start. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{parent1}] A changeset ID. The ID of the parent that the - working directory is to be updated to. If the working directory is - being merged, it will not change this parent. -\item[\texttt{parent2}] A changeset ID. Only set if the working - directory is being merged. The ID of the revision that the working - directory is being merged with. -\end{itemize} - -See also: \hook{update} (section~\ref{sec:hook:update}) - -\subsection{\hook{tag}---after tagging a changeset} -\label{sec:hook:tag} - -This hook is run after a tag has been created. - -Parameters to this hook: -\begin{itemize} -\item[\texttt{local}] A boolean. Whether the new tag is local to this - repository instance (i.e.~stored in \sfilename{.hg/localtags}) or - managed by Mercurial (stored in \sfilename{.hgtags}). -\item[\texttt{node}] A changeset ID. The ID of the changeset that was - tagged. -\item[\texttt{tag}] A string. The name of the tag that was created. -\end{itemize} - -If the created tag is revision-controlled, the \hook{commit} hook -(section~\ref{sec:hook:commit}) is run before this hook. - -See also: \hook{pretag} (section~\ref{sec:hook:pretag}) - -\subsection{\hook{update}---after updating or merging working directory} -\label{sec:hook:update} - -This hook is run after an update or merge of the working directory -completes. Since a merge can fail (if the external \command{hgmerge} -command fails to resolve conflicts in a file), this hook communicates -whether the update or merge completed cleanly. - -\begin{itemize} -\item[\texttt{error}] A boolean. Indicates whether the update or - merge completed successfully. -\item[\texttt{parent1}] A changeset ID. The ID of the parent that the - working directory was updated to. If the working directory was - merged, it will not have changed this parent. -\item[\texttt{parent2}] A changeset ID. Only set if the working - directory was merged. The ID of the revision that the working - directory was merged with. -\end{itemize} - -See also: \hook{preupdate} (section~\ref{sec:hook:preupdate}) - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/htlatex.book --- a/fr/htlatex.book Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#!/bin/bash -# -# This script is horrible. It's essentially a hacked copy of -# /usr/bin/htlatex from Fedora Core 6. I apologise for any lasting -# pain reading it causes. - -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -(cd $4 && bibtex hgbook) -(cd $4 && makeindex hgbook) -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -echo status $$ diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/intro.tex --- a/fr/intro.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,617 +0,0 @@ -\chapter{Introduction} -\label{chap:intro} - -\section{À propos de la gestion source} - -La gestion de sources est un processus permettant de gérer différentes -versions de la même information. Dans sa forme la plus simple, c'est -ce que tout le monde fait manuellement : quand vous modifiez -un fichier, vous le sauvegardez sous un nouveau nom contenant un numéro, -à chaque fois plus grand que celui de la version précédente. - -Ce genre de gestion de version manuelle est cependant facilement sujette -à des erreurs, ainsi, depuis longtemps, des logiciels existent pour -résoudre cette problématique. Les premiers outils de gestion de sources -étaient destinés à aider un seul utilisateur, à automatiser la gestion -des versions d'un seul fichier. Dans les dernières décades, cette cible -s'est largement agrandie, ils gèrent désormais de multiples fichiers, et -aident un grand nombre de personnes à travailler ensemble. Les outils les -plus modernes n'ont aucune difficulté à gérer plusieurs milliers de -personnes travaillant ensemble sur des projets regroupant plusieurs -centaines de milliers de fichiers. - -\subsection{Pourquoi utiliser un gestionnaire de source ?} - -Il y a de nombreuses raisons pour que vous ou votre équipe souhaitiez -utiliser un outil automatisant la gestion de version pour votre projet. -\begin{itemize} -\item L'outil se chargera de suivre l'évolution de votre projet, sans -que vous n'ayez à le faire. Pour chaque modification, vous aurez à votre -disposition un journal indiquant \emph{qui} a fait quoi, \emph{pourquoi} -ils l'ont fait, \emph{quand} ils l'ont fait, et \emph{ce} qu'ils ont -modifiés. -\item Quand vous travaillez avec d'autres personnes, les logiciels de -gestion de source facilitent le travail collaboratif. Par exemple, quand -plusieurs personnes font, plus ou moins simultanément, des modifications -incompatibles, le logiciel vous aidera à identifier et à résoudre les conflits. -\item L'outil vous aidera à réparer vos erreurs. Si vous effectuez un changement -qui se révèle être une erreur, vous pourrez revenir à une version -antérieure d'un fichier ou même d'un ensemble de fichiers. En fait, un outil de -gestion de source \emph{vraiment} efficace vous permettra d'identifier à quel -moment le problème est apparu (voir la section~\ref{sec:undo:bisect} pour plus -de détails). -\item L'outil vous permettra aussi de travailler sur plusieurs versions différentes -de votre projet et à gérer l'écart entre chacune. -\end{itemize} -La plupart de ces raisons ont autant d'importances ---du moins en théorie--- que -vous travailliez sur un projet pour vous, ou avec une centaine d'autres -personnes. - -Une question fondamentale à propos des outils de gestion de source, qu'il s'agisse -du projet d'une personne ou d'une grande équipe, est quels sont ses -\emph{avantages} par rapport à ses \emph{coûts}. Un outil qui est difficile à -utiliser ou à comprendre exigera un lourd effort d'adaptation. - -Un projet de cinq milles personnes s'effondrera très certainement de lui même -sans aucun processus et outil de gestion de source. Dans ce cas, le coût -d'utilisation d'un logiciel de gestion de source est dérisoire puisque -\emph{sans}, l'échec est presque garanti. - -D'un autre coté, un ``rapide hack'' d'une personne peut sembler un contexte -bien pauvre pour utiliser un outil de gestion de source, car, bien évidement -le coût d'utilisation dépasse le coût total du projet. N'est ce pas ? - -Mercurial supporte ces \emph{deux} échelles de travail. Vous pouvez apprendre -les bases en quelques minutes seulement, et, grâce à sa performance, vous pouvez -l'utiliser avec facilité sur le plus petit des projets. Cette simplicité -signifie que vous n'avez pas de concept obscurs ou de séquence de commandes -défiant l'imagination, sans aucune corrélation avec \emph{ce que vous êtes -vraiment en train de faire}. En même temps, ces mêmes performances et sa -nature ``peer-to-peer'' vous permettent d'augmenter, sans difficulté, son -utilisation à de très grands projets. - -Aucun outil de gestion de source ne peut sauver un projet mal mené, mais un -bon outil peut rendre beaucoup plus fluide votre travail. - -\subsection{Les multiples noms de la gestion de source} - -La gestion de source\footnote{NdT: J'ai utilisé systématiquement le terme -``gestion de source'' à travers tout l'ouvrage. Ce n'est pas forcement la -meilleure traduction, et ceci peut rendre la lecture un peu lourde, mais je -pense que le document y gagne en clarté et en précision.} est un domaine -divers, tellement qu'il n'existe pas une seul nom ou acronyme pour le désigner. -Voilà quelqu'uns des noms ou -acronymes que vous rencontrerez le plus souvent\footnote{NdT: J'ai conservé la -liste des noms en anglais pour des raisons de commodité (ils sont plus -``googelable''). En outre, j'ai opté pour conserver l'ensemble des opérations de -Mercurial (\textit{commit},\textit{push}, \textit{pull},...) en anglais, là -aussi pour faciliter la lecture d'autres documents en anglais, ainsi que -l'utilisation de Mercurial}. - -: -\begin{itemize} -\item \textit{Revision control (RCS)} ; -\item Software configuration management (SCM), ou \textit{configuration management} ; -\item \textit{Source code management} ; -\item \textit{Source code control}, ou \textit{source control} ; -\item \textit{Version control (VCS)}. -\end{itemize} - -Certaines personnes prétendent que ces termes ont en fait des sens -différents mais en pratique ils se recouvrent tellement qu'il n'y a pas -réellement de manière pertinente de les distinguer. - -\section{Une courte histoire de la gestion de source} - -Le plus célèbre des anciens outils de gestion de source est \textit{SCCS -(Source Code Control System)}, que Marc Rochkind conçu dans les laboratoires de -recherche de Bell (\textit{Bell Labs}), dans le début des années 70. -\textit{SCCS} ne fonctionnait que sur des fichiers individuels, et obligeait chaque -personne travaillant sur le projet d'avoir un accès à un répertoire de -travail commun, sur le même système. Seulement une seule personne pouvait -modifier un fichier au même moment, ce fonctionnement était assuré par -l'utilisation de verrou (``lock''). Il était courant que des personnes -verrouillent des fichiers, et plus tard, oublient de le déverrouiller; -empêchant n'importe qui d'autre de travailler sur ces fichiers sans l'aide de -l'administrateur... - -Walter Tichy a développé une alternative libre à \textit{SCCS} au début des -années 80, qu'il nomma \textit{RSC (Revison Control System)}. Comme -\textit{SCCS}, \textit{RCS} demandait aux développeurs de travailler sur le même -répertoire partagé, et de verrouiller les -fichiers pour se prémunir de tout conflit issu de modifications concurrentes. - -Un peu plus tard dans les années 1980, Dick Grune utilisa \textit{RCS} comme -une brique de base pour un ensemble de scripts \textit{shell} qu'il intitula -cmt, avant de la renommer en \textit{CVS (Concurrent Versions System)}. La -grande innovation de CVS était que les développeurs pouvaient travailler -simultanément et indépendamment dans leur propre espace de travail. Ces espaces -de travail privés assuraient que les développeurs ne se marchent pas -mutuellement sur les pieds, comme c'était souvent le cas avec RCS et SCCS. -Chaque développeur disposait donc de sa copie de tous les fichiers du projet, -et ils pouvaient donc librement les modifier. Ils devaient néanmoins effectuer -la ``fusion'' (\textit{``merge''}) de leurs fichiers, avant d'effectuer le -``commit'' de leur modifications sur le dépôt central. - -Brian Berliner reprit les scripts de Grune's et les réécrit en~C, qu'il publia -en 1989. Depuis, ce code a été modifié jusqu'à devenir la version moderne de -CVS. CVS a acquis ainsi la capacité de fonctionner en réseau, transformant son -architecture en client/serveur. L'architecture de CVS est centralisée, seul le -serveur a une copie de l'historique du projet. L'espace de travail client ne -contient qu'une copie de la dernière version du projet, et quelques métadonnées -pour indiquer où le serveur se trouve. CVS a été un grand succès, aujourd'hui -il est probablement l'outil de gestion de contrôle le plus utilisé au monde. - -Au début des années 1990, Sun Microsystmes développa un premier outil de -gestion de source distribué, nommé TeamWare. Un espace de travail TeamWare -contient une copie complète de l'historique du projet. TeamWare n'a pas de -notion de dépôt central. (CVS utilisait RCS pour le stockage de l'historique, -TeamWare utilisait SCCS). - -Alors que les années 1990 avançaient, les utilisateurs ont pris conscience d'un -certain nombre de problèmes avec CVS. Il enregistrait simultanément des -modifications sur différents fichiers individuellement, au lieu de les -regrouper dans une seule opération cohérente et atomique. Il ne gère pas bien -sa hiérarchie de fichier, il est donc assez aisé de créer le chaos en renommant -les fichiers et les répertoires. Pire encore, son code source est difficile à -lire et à maintenir, ce qui agrandit largement le ``niveau de souffrance'' -associé à la réparation de ces problèmes d'architecture de manière prohibitive. - -En 2001, Jim Blandy et Karl Fogel, deux développeurs qui avaient travaillé sur -CVS, initièrent un projet pour le remplacer par un outil qui aurait une -meilleure architecture et un code plus propre. Le résultat, Subversion, ne -quitte pas le modèle centralisé et client/server de CVS, mais ajoute les -opérations de ``commit'' atomique sur de multiples fichiers, une meilleure -gestion des espaces de noms, et d'autres fonctionnalités qui en font un -meilleur outil que CVS. Depuis sa première publication, il est rapidement -devenu très populaire. - -Plus ou moins simultanément, Graydon Hoare a commencé sur l'ambitieux -système de gestion distribué Monotone. Bien que Monotone corrige plusieurs -défauts de CVS's tout en offrant une architecture ``peer-to-peer'', il va aussi -plus loin que la plupart des outils de révision de manière assez innovante. Il -utilise des ``hash'' cryptographiques comme identifiants, et il a une notion -complète de ``confiance'' du code issu des différentes sources. - -Mercurial est né en 2005. Bien que très influencé par Monotone, Mercurial se -concentre sur la facilité d'utilisation, les performances et la capacité à -monter en charge pour de très gros projets. - -\section{Tendances de la gestion de source} - -Il y a eu une tendance évidente dans le développement et l'utilisation d'outils -de gestion de source depuis les quatre dernières décades, au fur et à mesure -que les utilisateurs se sont habitués à leur outils et se sont sentis contraints -par leurs limitations. - -La première génération commença simplement par gérer un fichier unique sur un -ordinateur individuel. Cependant, même si ces outils présentaient une grande -avancée par rapport à la gestion manuelle des versions, leur modèle de -verrouillage et leur utilisation limitée à un seul ordinateur rendaient leur -utilisation possible uniquement dans une très petite équipe. - -La seconde génération a assoupli ces contraintes en adoptant une architecture -réseau et centralisée, permettant de gérer plusieurs projets entiers en même -temps. Alors que les projets grandirent en taille, ils rencontrèrent de nouveaux -problèmes. Avec les clients discutant régulièrement avec le serveurs, la montée -en charge devint un réel problème sur les gros projets. Une connexion réseau -peu fiable pouvait complètement empêcher les utilisateurs distants de dialoguer -avec le serveur. Alors que les projets \textit{Open Source} commencèrent à -mettre en place des accès en lecture seule disponible anonymement, les -utilisateurs sans les privilèges de ``commit'' réalisèrent qu'ils ne pouvaient -pas utiliser les outils pour collaborer naturellement avec le projet, comme ils -ne pouvaient pas non plus enregistrer leurs modifications. - -La génération actuelle des outils de gestion de source est ``peer-to-peer'' par -nature. Tout ces systèmes ont abandonné la dépendance à un serveur central, et -ont permis à leur utilisateur de distribuer les données de leur gestion de -source à qui en a besoin. La collaboration à travers Internet a transformé la -contrainte technologique en une simple question de choix et de consencus. Les -outils modernes peuvent maintenant fonctionner en mode déconnecté sans limite et -de manière autonome, la connexion au réseau n'étant nécessaire que pour -synchroniser les modifications avec les autres dépôts. - -\section{Quelques avantages des gestionnaires de source distribués} - -Même si les gestionnaire de source distribués sont depuis plusieurs années -assez robustes et aussi utilisables que leurs prédécesseurs, les utilisateurs -d'autres outils n'y ont pas encore été sensibilisés. Les gestionnaires -de source distribués se distinguent particulièrement de leurs équivalents -centralisés de nombreuses manières. - -Pour un développeur individuel, ils restent beaucoup plus rapides que les -outils centralisés. Cela pour une raison simple : un outil centralisé doit -toujours dialoguer à travers le réseau pour la plupart des opérations, car -presque toutes les métadonnées sont stockées sur la seule copie du serveur -central. Un outil distribué stocke toute ses métadonnées localement. À tâche -égale, effectuer un échange avec le réseau ajoute un délai aux outils -centralisés. Ne sous-estimez pas la valeur d'un outil rapide : vous allez -passer beaucoup de temps à interagir avec un logiciel de gestion de source. - -Les outils distribués sont complètement indépendants des aléas de votre serveur, -d'autant plus qu'ils répliquent les métadonnées à beaucoup d'endroits. Si -votre serveur central prend feu, vous avez intérêt à ce que les médias de -sauvegardes soient fiables, et que votre dernier ``backup'' soit récent et -fonctionne sans problème. Avec un outil distribué, vous avez autant de -``backup'' que de contributeurs. - -En outre, la fiabilité de votre réseau affectera beaucoup moins les -outils distribués. Vous ne pouvez même pas utiliser un outil centralisé -sans connexion réseau, à l'exception de quelques commandes, très limitées. -Avec un outil distribué, si votre connexion réseau tombe pendant que vous -travaillez, vous pouvez ne même pas vous en rendre compte. La seule chose -que vous ne serez pas capable de faire sera de communiquer avec des dépôts -distants, opération somme toute assez rare en comparaison aux opérations -locales. Si vous avez une équipe de collaborateurs très dispersée ceci peut -être significatif. - -\subsection{Avantages pour les projets \textit{Open Source}} - -Si vous prenez goût à un projet \textit{Open Source} et que vous -décidez de commencer à toucher à son code, et que le projet utilise -un gestionnaire de source distribué, vous êtes immédiatement un "pair" -avec les personnes formant le ``cœur'' du projet. Si ils publient -leurs dépôts, vous pouvez immédiatement copier leurs historiques de -projet, faire des modifications, enregistrer votre travail en utilisant -les même outils qu'eux. Par comparaison, avec un outil centralisé, vous -devez utiliser un logiciel en mode ``lecture seule'' à moins que -quelqu'un ne vous donne les privilèges de ``commit'' sur le serveur -central. Avant ça, vous ne serez pas capable d'enregistrer vos -modifications, et vos propres modifications risqueront de se -corrompre chaque fois que vous essayerez de mettre à jour à votre -espace de travail avec le serveur central. - -\subsubsection{Le non-problème du \textit{fork}} - -Il a été souvent suggéré que les gestionnaires de source distribués -posent un risque pour les projets \textit{Open Source} car ils -facilitent grandement la création de ``fork''\footnote{NdT:Création -d'une -\url{version alternative du logiciel}{http://fr.wikipedia.org/wiki/Fork\#Embranchement\_d.27un\_projet\_informatique}.} -Un ``fork'' apparait quand il y des divergences d'opinion ou d'attitude -au sein d'un groupe de développeurs qui aboutissent à la décision de ne -plus travailler ensemble. Chaque parti s'empare d'une copie plus ou moins -complète du code source du projet et continue dans sa propre direction. - -Parfois ces différents partis décident de se réconcilier. Avec un -serveur central, l'aspect \emph{technique} de cette réconciliation -est un processus douloureux, et essentiellement manuel. Vous devez -décider quelle modification est ``la gagnante'', et replacer, par un -moyen ou un autre, les modifications de l'autre équipe dans l'arborescence -du projet. Ceci implique généralement la perte d'une partie de l'historique -d'un des partis, ou même des deux. - -Ce que les outils distribués permettent à ce sujet est probablement -la \emph{meilleure} façon de développer un projet. Chaque modification -que vous effectuez est potentiellement un ``fork''. La grande force de -cette approche est que les gestionnaires de source distribués doivent être -vraiment très efficaces pour \emph{fusionner}\footnote{NdT:j'ai choisi de -traduire ici \textit{merging} par ``fusionner'' pour des raisons de clarté} -des ``forks'', car les ``forks'', dans ce contexte, arrivent tout le -temps. - -Si chaque altération que n'importe qui effectue, à tout moment, est vue -comme un ``fork'' à fusionner, alors ce que le monde de l'\textit{Open -Source} voit comme un ``fork'' devient \emph{uniquement} une problématique -sociale. En fait, les outils de gestions de source distribués \emph{réduisent} -les chances de ``fork'': -\begin{itemize} -\item Ils éliminent la distinction sociale qu'imposent les outils centralisés - entre les membres du projets (ceux qui ont accès au ``commit'') et ceux de - l'extérieur (ce qui ne l'ont pas). \item Ils rendent plus facile la - réconciliation après un ``fork'' social, car - tout ce qu'elle implique est une simple fusion. -\end{itemize} - -Certaines personnes font de la résistance envers les gestionnaires de source -distribués parce qu'ils veulent garder un contrôle ferme sur leur projet, et -ils pensent que les outils centralisés leur fournissent ce contrôle. Néanmoins, -si c'est votre cas, sachez que si vous publiez votre dépôt CVS ou Subversion -de manière publique, il existe une quantité d'outils disponibles pour récupérer -entièrement votre projet et son historique (quoique lentement) et le récréer -ailleurs, sans votre contrôle. En fait, votre contrôle sur votre projet est -illusoire, vous ne faites qu'interdire à vos collaborateurs de travailler -de manière fluide, en disposant d'un miroir ou d'un ``fork'' de votre -historique. -%%%TODO: Fussy, those last sentences are not really well translated: -%%%no problem for me (wilk) -%However, if you're of this belief, and you publish your CVS or Subversion -%repositories publically, there are plenty of tools available that can pull -%out your entire project's history (albeit slowly) and recreate it somewhere -%that you don't control. So while your control in this case is illusory, you are -%forgoing the ability to fluidly collaborate with whatever people feel -%compelled to mirror and fork your history. - -\subsection{Avantages pour les projets commerciaux} - -Beaucoup de projets commerciaux sont réalisés par des équipes éparpillées -à travers le globe. Les contributeurs qui sont loin du serveur central -devront subir des commandes lentes et même parfois peu fiables. Les -solutions propriétaires de gestion de source tentent de palier ce problème -avec des réplications de sites distants qui sont à la fois coûteuses à mettre -en place et lourdes à administrer. Un système distribué ne souffre pas -de ce genre de problèmes. En outre, il est très aisé de mettre en place -plusieurs serveurs de références, disons un par site, de manière à ce qu'il -n'y ait pas de communication redondante entre les dépôts, sur une connexion -longue distance souvent onéreuse. - -Les systèmes de gestion de source supportent généralement assez mal la -montée en charge. Ce n'est pas rare pour un gestionnaire de source centralisé -pourtant onéreux de s'effondrer sous la charge combinée d'une douzaine -d'utilisateurs concurrents seulement. Une fois encore, la réponse à cette problématique -est généralement encore la mise en place d'un ensemble complexe de serveurs -synchronisés par un mécanisme de réplication. Dans le cas d'un gestionnaire -de source distribué, la charge du serveur central --- si vous avez un--- est -plusieurs fois inférieure (car toutes les données sont déjà répliquées ailleurs), -un simple serveur, pas très cher, peut gérer les besoins d'une plus grande -équipe, et la réplication pour balancer la charge devient le -travail d'un simple script. - -Si vous avez des employés sur le terrain, en train de chercher à résoudre un souci sur -le site d'un client, ils bénéficieront aussi d'un gestionnaire de source -distribué. Cet outil leur permettra de générer des versions personnalisées, -d'essayer différentes solutions, en les isolant aisément les unes des autres, -et de rechercher efficacement à travers l'historique des sources, la cause -des bugs ou des régressions, tout ceci sans avoir besoin de la moindre -connexion au réseau de votre compagnie. - -\section{Pourquoi choisir Mercurial?} - -Mercurial a plusieurs caractéristiques qui en font un choix particulièrement -pertinent pour la gestion de source: -\begin{itemize} - \item Il est facile à apprendre et à utiliser ; - \item Il est léger et performant ; - \item Il monte facilement en charge ; - \item Il est facile à personnaliser ; -\end{itemize} - -Si vous êtes déjà familier d'un outil de gestion de source, vous serez -capable de l'utiliser en moins de 5 minutes. Sinon, ça ne sera pas beaucoup -plus long\footnote{NdT: Pour appuyer le propos de l'auteur, je signale que -j'utilise Mercurial comme outil d'initiation à la gestion de contrôle dans -des travaux pratiques à l'ESME Sudria (\url{http://www.esme.fr}) et que les -élèves le prennent en main sans difficulté majeure malgré l'approche distribuée.}. -Les commandes utilisées par Mercurial, comme ses fonctionnalités, sont -généralement uniformes et cohérentes, et vous pouvez donc ainsi garder en tête -simplement quelques règles générales, plutôt qu'un lot complexe d'exceptions. - -Sur un petit projet, vous pouvez commencer à travailler avec Mercurial en -quelques instants. Ajouter des modifications ou des branches, transférer -ces modifications (localement ou via le réseau), et les opérations -d'historique ou de statut sont aussi très rapides. Mercurial reste hors de -votre chemin grâce à sa simplicité d'utilisation et sa rapidité d'exécution. - -L'utilité de Mercurial ne se limite pas à de petits projets: il est -aussi utilisé par des projets ayant des centaines ou même des milliers -de contributeurs, avec plusieurs dizaines de milliers de fichiers, et des -centaines de méga de code source. - -Voici une liste non exhaustive des projets complexes ou critiques utilisant -Mercurial : -%TODO -% For both spanish and english version, add the following examples: -\begin{itemize} - \item \url{Firefox}{https://developer.mozilla.org/en/Mozilla\_Source\_Code\_(Mercurial)} ; - \item \url{OpenSolaris}{http://opensolaris.org/os/community/tools/scm/hg\_help/} ; - \item \url{OpenJDK}{http://hg.openjdk.java.net/} (utilisant en outre l'extension - ``forest'' pour gérer ses sous modules); -\end{itemize} - -Si les fonctionnalités cœur de Mercurial ne sont pas suffisantes pour vous, -il est très aisé d'en construire d'autres. Mercurial est adapté à l'utilisation -de scripts, et son implémentation interne en Python, propre et claire, -rend encore plus facile l'ajout de fonctionnalités sous forme d'extensions. Il -en existe déjà un certain nombre de très populaires et très utiles, -dont le périmètre va de la recherche de bugs à l'amélioration des performances. - -\section{Mercurial comparé aux autres outils} - -Avant que vous n'alliez plus loin, comprenez bien que cette section -reflète mes propres expériences, et elle est donc (j'ose le dire) -peu objective. Néanmoins, j'ai utilisé les outils de gestion de source -listés ci dessous, dans la plupart des cas, pendant plusieurs années. -%% TODO: Fussy translation. - -\subsection{Subversion} - -Subversion est un des outils de gestion de source les plus populaire, il fût -développé pour remplacer CVS. Il a une architecture client/server centralisée. - -Subversion et Mercurial ont des noms de commandes très similaires pour -les mêmes opérations, ainsi si vous êtes familier avec l'un, c'est facile -d'apprendre l'autre. Ces deux outils sont portables sur les systèmes -d'exploitation les plus populaires\footnote{NdT:Mercurial fonctionne sans problème -sur OpenVMS à l'ESME Sudria \url{http://www.esme.fr}, compte tenu que Subversion a été -développé en C, je ne suis pas sûr que son portage aurait été aussi aisé.}. -%TODO: Backport this statement in english and spanish - -Avant la version 1.5, Subversion n'offrait aucune forme de support pour les fusions. Lors -de l'écriture de ce livre, ses capacités de fusion étaient nouvelles, et réputées pour être -\href{http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword}{complexes -et bugguées}. - -Mercurial dispose d'un avantage substantiel en terme de performance par rapport à -Subversion sur la plupart des opérations que j'ai pu tester. J'ai mesuré -une différence de performance allant de deux à six fois plus rapide avec -le système de stockage de fichier local de Subversion~1.4.3 -(\emph{ra\_local}), qui est la méthode d'accès la plus rapide disponible. Dans -un déploiement plus réaliste, impliquant un stockage réseau, Subversion -serait encore plus désavantagé. Parce que la plupart des commandes Subversion -doivent communiquer avec le serveur et que Subversion n'a pas de mécanisme -de réplication, la capacité du serveur et la bande passante sont devenues des -goulots d'étranglement pour les projets de taille moyenne ou grande. - -En outre, Subversion implique une surcharge substantielle dans le stockage local -de certaines données, pour éviter des transactions avec le serveur, pour -certaines opérations communes, telles que la recherche des fichiers modifiés -(\texttt{status}) et l'affichage des modifications par rapport à la révision -courante (\texttt{diff}). En conséquence, un répertoire de travail Subversion -a souvent la même taille, ou est plus grand, qu'un dépôt Mercurial et son -espace de travail, et ceci bien que le dépôt Mercurial contienne l'intégralité -de l'historique. - -Subversion est largement supporté par les outils tierces. Mercurial est -actuellement encore en retrait de ce point de vue. L'écart se réduit, néanmoins, -et en effet certains des outils graphiques sont maintenant supérieurs à leurs -équivalents Subversion. Comme Mercurial, Subversion dispose d'un excellent -manuel utilisateur. - -Parce que Subversion ne stocke pas l'historique chez ses clients, il est -parfaitement adapté à la gestion de projets qui doivent suivre un ensemble -de larges fichiers binaires et opaques. Si vous suivez une cinquantaine de -versions d'un fichier incompressible de 10MB, l'occupation disque coté client -d'un projet sous Subversion restera à peu près constante. A l'inverse, -l'occupation disque du même projet sous n'importe lequel des gestionnaires -de source distribués grandira rapidement, proportionnellement aux nombres -de versions, car les différences entre chaque révisions seront très grandes. - -En outre, c'est souvent difficile ou, généralement, impossible de fusionner -des différences dans un fichier binaire. La capacité de Subversion de -verrouiller des fichiers, pour permettre à l'utilisateur d'être le seul -à le mettre à jour (``commit'') temporairement, est un avantage significatif -dans un projet doté de beaucoup de fichiers binaires. - -Mercurial peut importer l'historique depuis un dépôt Subversion. Il peut -aussi exporter l'ensemble des révisions d'un projet vers un dépôt Subversion. -Ceci rend très facile de ``prendre la température'' et d'utiliser Mercurial et Subversion -en parallèle, avant de décider de migrer vers Mercurial. La conversion de -l'historique est incrémentale, donc vous pouvez effectuer une conversion -initiale, puis de petites additions par la suite pour ajouter les nouvelles -modifications. - -\subsection{Git} - -Git est un outil de gestion de source distribué qui fût développé pour gérer -le code source de noyau de Linux. Comme Mercurial, sa conception initiale a -été inspirée par Monotone. - -Git dispose d'un ensemble conséquent de commandes, avec plus de~139 commandes -individuelles pour la version~1.5.0. Il a aussi la réputation d'être difficile -à apprendre. Comparé à Git, le point fort de Mercurial est clairement sa -simplicité. - -En terme de performance, Git est extrêmement rapide. Dans la plupart des -cas, il est plus rapide que Mercurial, tout du moins sur Linux, alors que -Mercurial peut être plus performant sur d'autres opérations. Néanmoins, sur -Windows, les performances et le niveau de support général fourni par Git, -au moment de l'écriture de cet ouvrage, est bien derrière celui de Mercurial. - -Alors que le dépôt Mercurial ne demande aucune maintenance, un dépôt Git -exige d'exécuter manuellement et régulièrement la commande ``repacks'' sur -ces métadonnées. Sans ceci, les performances de git se dégradent et la -consommation de l'espace disque augmente rapidement. Un serveur qui contient -plusieurs dépôts Git qui ne sont pas régulièrement et fréquemment ``repacked'' -deviendra un vrai problème lors des ``backups'' du disque, et il y eu des -cas, où un ``backup'' journalier pouvait durer plus de~24 heures. Un dépôt -fraichement ``repacked'' sera légèrement plus petit qu'un dépôt Mercurial, -mais un dépôt non ``repacked'' est beaucoup plus grand. - -Le cœur de Git est écrit en C. La plupart des commandes Git sont implémentées -sous forme de scripts Shell ou Perl, et la qualité de ces scripts varie -grandement. J'ai plusieurs fois constaté que certains de ces scripts étaient -chargés en mémoire aveuglément et que la présence d'erreurs pouvait s'avérer -fatal. - -Mercurial peut importer l'historique d'un dépôt Git. - -\subsection{CVS} - -CVS est probablement l'outil de gestion de source le plus utilisé aujourd'hui -dans le monde. À cause de son manque de clarté interne, il n'est plus -maintenu depuis plusieurs années. - -Il a une architecture client/serveur centralisée. Il ne regroupe pas les -modifications de fichiers dans une opération de ``commit'' atomique, ce -qui permet à ses utilisateurs de ``casser le \textit{build}'' assez -facilement : une personne peut effectuer une opération de ``commit'' -sans problème puis être bloquée par besoin de fusion, avec comme conséquence -néfaste, que les autres utilisateurs ne récupèreront qu'une partie de ses -modifications. Ce problème affecte aussi la manière de travailler avec -l'historique du projet. Si vous voulez voir toutes les modifications d'une -personne du projet, vous devrez injecter manuellement les descriptions et les -\textit{timestamps} des modifications de chacun des fichiers impliqués (si -vous savez au moins quels sont ces fichiers). - -CVS a une notion étrange des \textit{tags} et des branches que je n'essayerai -même pas de décrire ici. Il ne supporte pas bien les opérations de renommage d'un -fichier ou d'un répertoire, ce qui facilite la corruption de son dépôt. Il n'a -presque pas pour ainsi dire de contrôle de cohérence interne, il est donc -pratiquement impossible de dire si un dépôt est corrompu ni à quel point. Je -ne recommanderai pas CVS pour un projet existant ou nouveau. - -Mercurial peut importer l'historique d'un projet CVS. Néanmoins, il y a -quelques principes à respecter; ce qui est vrai aussi pour les autres -outils d'import de projet CVS. À cause de l'absence de ``commit'' atomique -et gestion de version de l'arborescence, il n'est pas possible de reconstruire -de manière précise l'ensemble de l'historique. Un travail de ``devinette'' -est donc nécessaire, et les fichiers renommés ne sont pas détectés. Parce -qu'une bonne part de l'administration d'un dépôt CVS est effectuée manuellement, -et est donc, sujette à erreur, il est courant que les imports CVS rencontrent -de nombreux problèmes avec les dépôt corrompus (des \textit{timestamps} -de révision complètement buggés et des fichiers verrouillés depuis des années -sont deux des problèmes les moins intéressants dont je me souvienne). - -Mercurial peut importer l'historique depuis un dépôt CVS. - -\subsection{Outils propriétaires} - -Perforce a une architecture client/serveur centralisée, sans aucun -mécanisme de mise en cache de données coté client. Contrairement à la plupart -des outils modernes de gestion de source, Perforce exige de ses -utilisateurs d'exécuter une commande pour informer le serveur -central de tout fichier qu'ils souhaitent modifier. - -Les performances de Perforce sont plutôt bonnes pour des petites -équipes, mais elles s'effondrent rapidement lorsque le nombre -d'utilisateurs augmente au delà de la douzaine. Des installations -de Perforce assez larges nécessitent le déploiement de proxies pour -supporter la montée en charge associée. - -\subsection{Choisir un outil de gestion de source} - -A l'exception de CVS, tous les outils listés ci-dessus ont des -forces qui leur sont propres et qui correspondent à certaines -formes de projet. Il n'y a pas un seul meilleur outil de gestion -de source qui correspondrait le mieux à toutes les situations. - -Par exemple, Subversion est un très bon choix lorsqu'on travaille -avec beaucoup de fichiers binaires, qui évoluent régulièrement, grâce -à sa nature centralisée et sa capacité à verrouiller des fichiers. - -Personnellement, je préfère Mercurial pour sa simplicité, ses -performances et sa bonne capacité de fusion, et il m'a très bien rendu service -de plusieurs années maintenant. - -\section{Migrer depuis un outil à Mercurial} - -Mercurial est livré avec une extension nommée \hgext{convert}, qui -peut de manière incrémentale importer des révisions depuis différents -autres outils de gestion de source. Par ``incrémental'', j'entends que -vous pouvez convertir l'historique entier du projet en une seule fois, -puis relancer l'outil d'import plus tard pour obtenir les modifications -effectuées depuis votre import initial. - -Les outils de gestion de source supportés par \hgext{convert} sont : -\begin{itemize} - \item Subversion - \item CVS - \item Git - \item Darcs -\end{itemize} - -En outre, \hgext{convert} peut exporter les modifications depuis Mercurial -vers Subversion. Ceci rend possible d'essayer Subversion en parallèle -avant de choisir une solution définitive, sans aucun risque de perte de -données. - -La commande \hgxcmd{conver}{convert} est très simple à utiliser. Simplement, -indiquez le chemin ou l'URL du dépôt de source, en lui indiquant éventuellement -le nom du chemin de destination, et la conversion se met en route. Après cet -import initial, il suffit de relancer la commande encore une fois pour -importer les modifications effectuées depuis. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/kdiff3.png Binary file fr/kdiff3.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/license.tex --- a/fr/license.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -\chapter{Open Publication License} -\label{cha:opl} - -Version 1.0, 8 June 1999 - -\section{Requirements on both unmodified and modified versions} - -The Open Publication works may be reproduced and distributed in whole -or in part, in any medium physical or electronic, provided that the -terms of this license are adhered to, and that this license or an -incorporation of it by reference (with any options elected by the -author(s) and/or publisher) is displayed in the reproduction. - -Proper form for an incorporation by reference is as follows: - -\begin{quote} - Copyright (c) \emph{year} by \emph{author's name or designee}. This - material may be distributed only subject to the terms and conditions - set forth in the Open Publication License, v\emph{x.y} or later (the - latest version is presently available at - \url{http://www.opencontent.org/openpub/}). -\end{quote} - -The reference must be immediately followed with any options elected by -the author(s) and/or publisher of the document (see -section~\ref{sec:opl:options}). - -Commercial redistribution of Open Publication-licensed material is -permitted. - -Any publication in standard (paper) book form shall require the -citation of the original publisher and author. The publisher and -author's names shall appear on all outer surfaces of the book. On all -outer surfaces of the book the original publisher's name shall be as -large as the title of the work and cited as possessive with respect to -the title. - -\section{Copyright} - -The copyright to each Open Publication is owned by its author(s) or -designee. - -\section{Scope of license} - -The following license terms apply to all Open Publication works, -unless otherwise explicitly stated in the document. - -Mere aggregation of Open Publication works or a portion of an Open -Publication work with other works or programs on the same media shall -not cause this license to apply to those other works. The aggregate -work shall contain a notice specifying the inclusion of the Open -Publication material and appropriate copyright notice. - -\textbf{Severability}. If any part of this license is found to be -unenforceable in any jurisdiction, the remaining portions of the -license remain in force. - -\textbf{No warranty}. Open Publication works are licensed and provided -``as is'' without warranty of any kind, express or implied, including, -but not limited to, the implied warranties of merchantability and -fitness for a particular purpose or a warranty of non-infringement. - -\section{Requirements on modified works} - -All modified versions of documents covered by this license, including -translations, anthologies, compilations and partial documents, must -meet the following requirements: - -\begin{enumerate} -\item The modified version must be labeled as such. -\item The person making the modifications must be identified and the - modifications dated. -\item Acknowledgement of the original author and publisher if - applicable must be retained according to normal academic citation - practices. -\item The location of the original unmodified document must be - identified. -\item The original author's (or authors') name(s) may not be used to - assert or imply endorsement of the resulting document without the - original author's (or authors') permission. -\end{enumerate} - -\section{Good-practice recommendations} - -In addition to the requirements of this license, it is requested from -and strongly recommended of redistributors that: - -\begin{enumerate} -\item If you are distributing Open Publication works on hardcopy or - CD-ROM, you provide email notification to the authors of your intent - to redistribute at least thirty days before your manuscript or media - freeze, to give the authors time to provide updated documents. This - notification should describe modifications, if any, made to the - document. -\item All substantive modifications (including deletions) be either - clearly marked up in the document or else described in an attachment - to the document. -\item Finally, while it is not mandatory under this license, it is - considered good form to offer a free copy of any hardcopy and CD-ROM - expression of an Open Publication-licensed work to its author(s). -\end{enumerate} - -\section{License options} -\label{sec:opl:options} - -The author(s) and/or publisher of an Open Publication-licensed -document may elect certain options by appending language to the -reference to or copy of the license. These options are considered part -of the license instance and must be included with the license (or its -incorporation by reference) in derived works. - -\begin{enumerate}[A] -\item To prohibit distribution of substantively modified versions - without the explicit permission of the author(s). ``Substantive - modification'' is defined as a change to the semantic content of the - document, and excludes mere changes in format or typographical - corrections. - - To accomplish this, add the phrase ``Distribution of substantively - modified versions of this document is prohibited without the - explicit permission of the copyright holder.'' to the license - reference or copy. - -\item To prohibit any publication of this work or derivative works in - whole or in part in standard (paper) book form for commercial - purposes is prohibited unless prior permission is obtained from the - copyright holder. - - To accomplish this, add the phrase ``Distribution of the work or - derivative of the work in any standard (paper) book form is - prohibited unless prior permission is obtained from the copyright - holder.'' to the license reference or copy. -\end{enumerate} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/metadata.svg --- a/fr/metadata.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,328 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Changelog - Manifest - Filelogs - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/mq-collab.tex --- a/fr/mq-collab.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,393 +0,0 @@ -\chapter{Advanced uses of Mercurial Queues} -\label{chap:mq-collab} - -While it's easy to pick up straightforward uses of Mercurial Queues, -use of a little discipline and some of MQ's less frequently used -capabilities makes it possible to work in complicated development -environments. - -In this chapter, I will use as an example a technique I have used to -manage the development of an Infiniband device driver for the Linux -kernel. The driver in question is large (at least as drivers go), -with 25,000 lines of code spread across 35 source files. It is -maintained by a small team of developers. - -While much of the material in this chapter is specific to Linux, the -same principles apply to any code base for which you're not the -primary owner, and upon which you need to do a lot of development. - -\section{The problem of many targets} - -The Linux kernel changes rapidly, and has never been internally -stable; developers frequently make drastic changes between releases. -This means that a version of the driver that works well with a -particular released version of the kernel will not even \emph{compile} -correctly against, typically, any other version. - -To maintain a driver, we have to keep a number of distinct versions of -Linux in mind. -\begin{itemize} -\item One target is the main Linux kernel development tree. - Maintenance of the code is in this case partly shared by other - developers in the kernel community, who make ``drive-by'' - modifications to the driver as they develop and refine kernel - subsystems. -\item We also maintain a number of ``backports'' to older versions of - the Linux kernel, to support the needs of customers who are running - older Linux distributions that do not incorporate our drivers. (To - \emph{backport} a piece of code is to modify it to work in an older - version of its target environment than the version it was developed - for.) -\item Finally, we make software releases on a schedule that is - necessarily not aligned with those used by Linux distributors and - kernel developers, so that we can deliver new features to customers - without forcing them to upgrade their entire kernels or - distributions. -\end{itemize} - -\subsection{Tempting approaches that don't work well} - -There are two ``standard'' ways to maintain a piece of software that -has to target many different environments. - -The first is to maintain a number of branches, each intended for a -single target. The trouble with this approach is that you must -maintain iron discipline in the flow of changes between repositories. -A new feature or bug fix must start life in a ``pristine'' repository, -then percolate out to every backport repository. Backport changes are -more limited in the branches they should propagate to; a backport -change that is applied to a branch where it doesn't belong will -probably stop the driver from compiling. - -The second is to maintain a single source tree filled with conditional -statements that turn chunks of code on or off depending on the -intended target. Because these ``ifdefs'' are not allowed in the -Linux kernel tree, a manual or automatic process must be followed to -strip them out and yield a clean tree. A code base maintained in this -fashion rapidly becomes a rat's nest of conditional blocks that are -difficult to understand and maintain. - -Neither of these approaches is well suited to a situation where you -don't ``own'' the canonical copy of a source tree. In the case of a -Linux driver that is distributed with the standard kernel, Linus's -tree contains the copy of the code that will be treated by the world -as canonical. The upstream version of ``my'' driver can be modified -by people I don't know, without me even finding out about it until -after the changes show up in Linus's tree. - -These approaches have the added weakness of making it difficult to -generate well-formed patches to submit upstream. - -In principle, Mercurial Queues seems like a good candidate to manage a -development scenario such as the above. While this is indeed the -case, MQ contains a few added features that make the job more -pleasant. - -\section{Conditionally applying patches with - guards} - -Perhaps the best way to maintain sanity with so many targets is to be -able to choose specific patches to apply for a given situation. MQ -provides a feature called ``guards'' (which originates with quilt's -\texttt{guards} command) that does just this. To start off, let's -create a simple repository for experimenting in. -\interaction{mq.guards.init} -This gives us a tiny repository that contains two patches that don't -have any dependencies on each other, because they touch different files. - -The idea behind conditional application is that you can ``tag'' a -patch with a \emph{guard}, which is simply a text string of your -choosing, then tell MQ to select specific guards to use when applying -patches. MQ will then either apply, or skip over, a guarded patch, -depending on the guards that you have selected. - -A patch can have an arbitrary number of guards; -each one is \emph{positive} (``apply this patch if this guard is -selected'') or \emph{negative} (``skip this patch if this guard is -selected''). A patch with no guards is always applied. - -\section{Controlling the guards on a patch} - -The \hgxcmd{mq}{qguard} command lets you determine which guards should -apply to a patch, or display the guards that are already in effect. -Without any arguments, it displays the guards on the current topmost -patch. -\interaction{mq.guards.qguard} -To set a positive guard on a patch, prefix the name of the guard with -a ``\texttt{+}''. -\interaction{mq.guards.qguard.pos} -To set a negative guard on a patch, prefix the name of the guard with -a ``\texttt{-}''. -\interaction{mq.guards.qguard.neg} - -\begin{note} - The \hgxcmd{mq}{qguard} command \emph{sets} the guards on a patch; it - doesn't \emph{modify} them. What this means is that if you run - \hgcmdargs{qguard}{+a +b} on a patch, then \hgcmdargs{qguard}{+c} on - the same patch, the \emph{only} guard that will be set on it - afterwards is \texttt{+c}. -\end{note} - -Mercurial stores guards in the \sfilename{series} file; the form in -which they are stored is easy both to understand and to edit by hand. -(In other words, you don't have to use the \hgxcmd{mq}{qguard} command if -you don't want to; it's okay to simply edit the \sfilename{series} -file.) -\interaction{mq.guards.series} - -\section{Selecting the guards to use} - -The \hgxcmd{mq}{qselect} command determines which guards are active at a -given time. The effect of this is to determine which patches MQ will -apply the next time you run \hgxcmd{mq}{qpush}. It has no other effect; in -particular, it doesn't do anything to patches that are already -applied. - -With no arguments, the \hgxcmd{mq}{qselect} command lists the guards -currently in effect, one per line of output. Each argument is treated -as the name of a guard to apply. -\interaction{mq.guards.qselect.foo} -In case you're interested, the currently selected guards are stored in -the \sfilename{guards} file. -\interaction{mq.guards.qselect.cat} -We can see the effect the selected guards have when we run -\hgxcmd{mq}{qpush}. -\interaction{mq.guards.qselect.qpush} - -A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}'' -character. The name of a guard must not contain white space, but most -other characters are acceptable. If you try to use a guard with an -invalid name, MQ will complain: -\interaction{mq.guards.qselect.error} -Changing the selected guards changes the patches that are applied. -\interaction{mq.guards.qselect.quux} -You can see in the example below that negative guards take precedence -over positive guards. -\interaction{mq.guards.qselect.foobar} - -\section{MQ's rules for applying patches} - -The rules that MQ uses when deciding whether to apply a patch -are as follows. -\begin{itemize} -\item A patch that has no guards is always applied. -\item If the patch has any negative guard that matches any currently - selected guard, the patch is skipped. -\item If the patch has any positive guard that matches any currently - selected guard, the patch is applied. -\item If the patch has positive or negative guards, but none matches - any currently selected guard, the patch is skipped. -\end{itemize} - -\section{Trimming the work environment} - -In working on the device driver I mentioned earlier, I don't apply the -patches to a normal Linux kernel tree. Instead, I use a repository -that contains only a snapshot of the source files and headers that are -relevant to Infiniband development. This repository is~1\% the size -of a kernel repository, so it's easier to work with. - -I then choose a ``base'' version on top of which the patches are -applied. This is a snapshot of the Linux kernel tree as of a revision -of my choosing. When I take the snapshot, I record the changeset ID -from the kernel repository in the commit message. Since the snapshot -preserves the ``shape'' and content of the relevant parts of the -kernel tree, I can apply my patches on top of either my tiny -repository or a normal kernel tree. - -Normally, the base tree atop which the patches apply should be a -snapshot of a very recent upstream tree. This best facilitates the -development of patches that can easily be submitted upstream with few -or no modifications. - -\section{Dividing up the \sfilename{series} file} - -I categorise the patches in the \sfilename{series} file into a number -of logical groups. Each section of like patches begins with a block -of comments that describes the purpose of the patches that follow. - -The sequence of patch groups that I maintain follows. The ordering of -these groups is important; I'll describe why after I introduce the -groups. -\begin{itemize} -\item The ``accepted'' group. Patches that the development team has - submitted to the maintainer of the Infiniband subsystem, and which - he has accepted, but which are not present in the snapshot that the - tiny repository is based on. These are ``read only'' patches, - present only to transform the tree into a similar state as it is in - the upstream maintainer's repository. -\item The ``rework'' group. Patches that I have submitted, but that - the upstream maintainer has requested modifications to before he - will accept them. -\item The ``pending'' group. Patches that I have not yet submitted to - the upstream maintainer, but which we have finished working on. - These will be ``read only'' for a while. If the upstream maintainer - accepts them upon submission, I'll move them to the end of the - ``accepted'' group. If he requests that I modify any, I'll move - them to the beginning of the ``rework'' group. -\item The ``in progress'' group. Patches that are actively being - developed, and should not be submitted anywhere yet. -\item The ``backport'' group. Patches that adapt the source tree to - older versions of the kernel tree. -\item The ``do not ship'' group. Patches that for some reason should - never be submitted upstream. For example, one such patch might - change embedded driver identification strings to make it easier to - distinguish, in the field, between an out-of-tree version of the - driver and a version shipped by a distribution vendor. -\end{itemize} - -Now to return to the reasons for ordering groups of patches in this -way. We would like the lowest patches in the stack to be as stable as -possible, so that we will not need to rework higher patches due to -changes in context. Putting patches that will never be changed first -in the \sfilename{series} file serves this purpose. - -We would also like the patches that we know we'll need to modify to be -applied on top of a source tree that resembles the upstream tree as -closely as possible. This is why we keep accepted patches around for -a while. - -The ``backport'' and ``do not ship'' patches float at the end of the -\sfilename{series} file. The backport patches must be applied on top -of all other patches, and the ``do not ship'' patches might as well -stay out of harm's way. - -\section{Maintaining the patch series} - -In my work, I use a number of guards to control which patches are to -be applied. - -\begin{itemize} -\item ``Accepted'' patches are guarded with \texttt{accepted}. I - enable this guard most of the time. When I'm applying the patches - on top of a tree where the patches are already present, I can turn - this patch off, and the patches that follow it will apply cleanly. -\item Patches that are ``finished'', but not yet submitted, have no - guards. If I'm applying the patch stack to a copy of the upstream - tree, I don't need to enable any guards in order to get a reasonably - safe source tree. -\item Those patches that need reworking before being resubmitted are - guarded with \texttt{rework}. -\item For those patches that are still under development, I use - \texttt{devel}. -\item A backport patch may have several guards, one for each version - of the kernel to which it applies. For example, a patch that - backports a piece of code to~2.6.9 will have a~\texttt{2.6.9} guard. -\end{itemize} -This variety of guards gives me considerable flexibility in -determining what kind of source tree I want to end up with. For most -situations, the selection of appropriate guards is automated during -the build process, but I can manually tune the guards to use for less -common circumstances. - -\subsection{The art of writing backport patches} - -Using MQ, writing a backport patch is a simple process. All such a -patch has to do is modify a piece of code that uses a kernel feature -not present in the older version of the kernel, so that the driver -continues to work correctly under that older version. - -A useful goal when writing a good backport patch is to make your code -look as if it was written for the older version of the kernel you're -targeting. The less obtrusive the patch, the easier it will be to -understand and maintain. If you're writing a collection of backport -patches to avoid the ``rat's nest'' effect of lots of -\texttt{\#ifdef}s (hunks of source code that are only used -conditionally) in your code, don't introduce version-dependent -\texttt{\#ifdef}s into the patches. Instead, write several patches, -each of which makes unconditional changes, and control their -application using guards. - -There are two reasons to divide backport patches into a distinct -group, away from the ``regular'' patches whose effects they modify. -The first is that intermingling the two makes it more difficult to use -a tool like the \hgext{patchbomb} extension to automate the process of -submitting the patches to an upstream maintainer. The second is that -a backport patch could perturb the context in which a subsequent -regular patch is applied, making it impossible to apply the regular -patch cleanly \emph{without} the earlier backport patch already being -applied. - -\section{Useful tips for developing with MQ} - -\subsection{Organising patches in directories} - -If you're working on a substantial project with MQ, it's not difficult -to accumulate a large number of patches. For example, I have one -patch repository that contains over 250 patches. - -If you can group these patches into separate logical categories, you -can if you like store them in different directories; MQ has no -problems with patch names that contain path separators. - -\subsection{Viewing the history of a patch} -\label{mq-collab:tips:interdiff} - -If you're developing a set of patches over a long time, it's a good -idea to maintain them in a repository, as discussed in -section~\ref{sec:mq:repo}. If you do so, you'll quickly discover that -using the \hgcmd{diff} command to look at the history of changes to a -patch is unworkable. This is in part because you're looking at the -second derivative of the real code (a diff of a diff), but also -because MQ adds noise to the process by modifying time stamps and -directory names when it updates a patch. - -However, you can use the \hgext{extdiff} extension, which is bundled -with Mercurial, to turn a diff of two versions of a patch into -something readable. To do this, you will need a third-party package -called \package{patchutils}~\cite{web:patchutils}. This provides a -command named \command{interdiff}, which shows the differences between -two diffs as a diff. Used on two versions of the same diff, it -generates a diff that represents the diff from the first to the second -version. - -You can enable the \hgext{extdiff} extension in the usual way, by -adding a line to the \rcsection{extensions} section of your \hgrc. -\begin{codesample2} - [extensions] - extdiff = -\end{codesample2} -The \command{interdiff} command expects to be passed the names of two -files, but the \hgext{extdiff} extension passes the program it runs a -pair of directories, each of which can contain an arbitrary number of -files. We thus need a small program that will run \command{interdiff} -on each pair of files in these two directories. This program is -available as \sfilename{hg-interdiff} in the \dirname{examples} -directory of the source code repository that accompanies this book. -\excode{hg-interdiff} - -With the \sfilename{hg-interdiff} program in your shell's search path, -you can run it as follows, from inside an MQ patch directory: -\begin{codesample2} - hg extdiff -p hg-interdiff -r A:B my-change.patch -\end{codesample2} -Since you'll probably want to use this long-winded command a lot, you -can get \hgext{hgext} to make it available as a normal Mercurial -command, again by editing your \hgrc. -\begin{codesample2} - [extdiff] - cmd.interdiff = hg-interdiff -\end{codesample2} -This directs \hgext{hgext} to make an \texttt{interdiff} command -available, so you can now shorten the previous invocation of -\hgxcmd{extdiff}{extdiff} to something a little more wieldy. -\begin{codesample2} - hg interdiff -r A:B my-change.patch -\end{codesample2} - -\begin{note} - The \command{interdiff} command works well only if the underlying - files against which versions of a patch are generated remain the - same. If you create a patch, modify the underlying files, and then - regenerate the patch, \command{interdiff} may not produce useful - output. -\end{note} - -The \hgext{extdiff} extension is useful for more than merely improving -the presentation of MQ~patches. To read more about it, go to -section~\ref{sec:hgext:extdiff}. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/mq-ref.tex --- a/fr/mq-ref.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,349 +0,0 @@ -\chapter{Mercurial Queues reference} -\label{chap:mqref} - -\section{MQ command reference} -\label{sec:mqref:cmdref} - -For an overview of the commands provided by MQ, use the command -\hgcmdargs{help}{mq}. - -\subsection{\hgxcmd{mq}{qapplied}---print applied patches} - -The \hgxcmd{mq}{qapplied} command prints the current stack of applied -patches. Patches are printed in oldest-to-newest order, so the last -patch in the list is the ``top'' patch. - -\subsection{\hgxcmd{mq}{qcommit}---commit changes in the queue repository} - -The \hgxcmd{mq}{qcommit} command commits any outstanding changes in the -\sdirname{.hg/patches} repository. This command only works if the -\sdirname{.hg/patches} directory is a repository, i.e.~you created the -directory using \hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} or ran -\hgcmd{init} in the directory after running \hgxcmd{mq}{qinit}. - -This command is shorthand for \hgcmdargs{commit}{--cwd .hg/patches}. - -\subsection{\hgxcmd{mq}{qdelete}---delete a patch from the - \sfilename{series} file} - -The \hgxcmd{mq}{qdelete} command removes the entry for a patch from the -\sfilename{series} file in the \sdirname{.hg/patches} directory. It -does not pop the patch if the patch is already applied. By default, -it does not delete the patch file; use the \hgxopt{mq}{qdel}{-f} option to -do that. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qdel}{-f}] Delete the patch file. -\end{itemize} - -\subsection{\hgxcmd{mq}{qdiff}---print a diff of the topmost applied patch} - -The \hgxcmd{mq}{qdiff} command prints a diff of the topmost applied patch. -It is equivalent to \hgcmdargs{diff}{-r-2:-1}. - -\subsection{\hgxcmd{mq}{qfold}---merge (``fold'') several patches into one} - -The \hgxcmd{mq}{qfold} command merges multiple patches into the topmost -applied patch, so that the topmost applied patch makes the union of -all of the changes in the patches in question. - -The patches to fold must not be applied; \hgxcmd{mq}{qfold} will exit with -an error if any is. The order in which patches are folded is -significant; \hgcmdargs{qfold}{a b} means ``apply the current topmost -patch, followed by \texttt{a}, followed by \texttt{b}''. - -The comments from the folded patches are appended to the comments of -the destination patch, with each block of comments separated by three -asterisk (``\texttt{*}'') characters. Use the \hgxopt{mq}{qfold}{-e} -option to edit the commit message for the combined patch/changeset -after the folding has completed. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qfold}{-e}] Edit the commit message and patch description - for the newly folded patch. -\item[\hgxopt{mq}{qfold}{-l}] Use the contents of the given file as the new - commit message and patch description for the folded patch. -\item[\hgxopt{mq}{qfold}{-m}] Use the given text as the new commit message - and patch description for the folded patch. -\end{itemize} - -\subsection{\hgxcmd{mq}{qheader}---display the header/description of a patch} - -The \hgxcmd{mq}{qheader} command prints the header, or description, of a -patch. By default, it prints the header of the topmost applied patch. -Given an argument, it prints the header of the named patch. - -\subsection{\hgxcmd{mq}{qimport}---import a third-party patch into the queue} - -The \hgxcmd{mq}{qimport} command adds an entry for an external patch to the -\sfilename{series} file, and copies the patch into the -\sdirname{.hg/patches} directory. It adds the entry immediately after -the topmost applied patch, but does not push the patch. - -If the \sdirname{.hg/patches} directory is a repository, -\hgxcmd{mq}{qimport} automatically does an \hgcmd{add} of the imported -patch. - -\subsection{\hgxcmd{mq}{qinit}---prepare a repository to work with MQ} - -The \hgxcmd{mq}{qinit} command prepares a repository to work with MQ. It -creates a directory called \sdirname{.hg/patches}. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qinit}{-c}] Create \sdirname{.hg/patches} as a repository - in its own right. Also creates a \sfilename{.hgignore} file that - will ignore the \sfilename{status} file. -\end{itemize} - -When the \sdirname{.hg/patches} directory is a repository, the -\hgxcmd{mq}{qimport} and \hgxcmd{mq}{qnew} commands automatically \hgcmd{add} -new patches. - -\subsection{\hgxcmd{mq}{qnew}---create a new patch} - -The \hgxcmd{mq}{qnew} command creates a new patch. It takes one mandatory -argument, the name to use for the patch file. The newly created patch -is created empty by default. It is added to the \sfilename{series} -file after the current topmost applied patch, and is immediately -pushed on top of that patch. - -If \hgxcmd{mq}{qnew} finds modified files in the working directory, it will -refuse to create a new patch unless the \hgxopt{mq}{qnew}{-f} option is -used (see below). This behaviour allows you to \hgxcmd{mq}{qrefresh} your -topmost applied patch before you apply a new patch on top of it. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qnew}{-f}] Create a new patch if the contents of the - working directory are modified. Any outstanding modifications are - added to the newly created patch, so after this command completes, - the working directory will no longer be modified. -\item[\hgxopt{mq}{qnew}{-m}] Use the given text as the commit message. - This text will be stored at the beginning of the patch file, before - the patch data. -\end{itemize} - -\subsection{\hgxcmd{mq}{qnext}---print the name of the next patch} - -The \hgxcmd{mq}{qnext} command prints the name name of the next patch in -the \sfilename{series} file after the topmost applied patch. This -patch will become the topmost applied patch if you run \hgxcmd{mq}{qpush}. - -\subsection{\hgxcmd{mq}{qpop}---pop patches off the stack} - -The \hgxcmd{mq}{qpop} command removes applied patches from the top of the -stack of applied patches. By default, it removes only one patch. - -This command removes the changesets that represent the popped patches -from the repository, and updates the working directory to undo the -effects of the patches. - -This command takes an optional argument, which it uses as the name or -index of the patch to pop to. If given a name, it will pop patches -until the named patch is the topmost applied patch. If given a -number, \hgxcmd{mq}{qpop} treats the number as an index into the entries in -the series file, counting from zero (empty lines and lines containing -only comments do not count). It pops patches until the patch -identified by the given index is the topmost applied patch. - -The \hgxcmd{mq}{qpop} command does not read or write patches or the -\sfilename{series} file. It is thus safe to \hgxcmd{mq}{qpop} a patch that -you have removed from the \sfilename{series} file, or a patch that you -have renamed or deleted entirely. In the latter two cases, use the -name of the patch as it was when you applied it. - -By default, the \hgxcmd{mq}{qpop} command will not pop any patches if the -working directory has been modified. You can override this behaviour -using the \hgxopt{mq}{qpop}{-f} option, which reverts all modifications in -the working directory. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qpop}{-a}] Pop all applied patches. This returns the - repository to its state before you applied any patches. -\item[\hgxopt{mq}{qpop}{-f}] Forcibly revert any modifications to the - working directory when popping. -\item[\hgxopt{mq}{qpop}{-n}] Pop a patch from the named queue. -\end{itemize} - -The \hgxcmd{mq}{qpop} command removes one line from the end of the -\sfilename{status} file for each patch that it pops. - -\subsection{\hgxcmd{mq}{qprev}---print the name of the previous patch} - -The \hgxcmd{mq}{qprev} command prints the name of the patch in the -\sfilename{series} file that comes before the topmost applied patch. -This will become the topmost applied patch if you run \hgxcmd{mq}{qpop}. - -\subsection{\hgxcmd{mq}{qpush}---push patches onto the stack} -\label{sec:mqref:cmd:qpush} - -The \hgxcmd{mq}{qpush} command adds patches onto the applied stack. By -default, it adds only one patch. - -This command creates a new changeset to represent each applied patch, -and updates the working directory to apply the effects of the patches. - -The default data used when creating a changeset are as follows: -\begin{itemize} -\item The commit date and time zone are the current date and time - zone. Because these data are used to compute the identity of a - changeset, this means that if you \hgxcmd{mq}{qpop} a patch and - \hgxcmd{mq}{qpush} it again, the changeset that you push will have a - different identity than the changeset you popped. -\item The author is the same as the default used by the \hgcmd{commit} - command. -\item The commit message is any text from the patch file that comes - before the first diff header. If there is no such text, a default - commit message is used that identifies the name of the patch. -\end{itemize} -If a patch contains a Mercurial patch header (XXX add link), the -information in the patch header overrides these defaults. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qpush}{-a}] Push all unapplied patches from the - \sfilename{series} file until there are none left to push. -\item[\hgxopt{mq}{qpush}{-l}] Add the name of the patch to the end - of the commit message. -\item[\hgxopt{mq}{qpush}{-m}] If a patch fails to apply cleanly, use the - entry for the patch in another saved queue to compute the parameters - for a three-way merge, and perform a three-way merge using the - normal Mercurial merge machinery. Use the resolution of the merge - as the new patch content. -\item[\hgxopt{mq}{qpush}{-n}] Use the named queue if merging while pushing. -\end{itemize} - -The \hgxcmd{mq}{qpush} command reads, but does not modify, the -\sfilename{series} file. It appends one line to the \hgcmd{status} -file for each patch that it pushes. - -\subsection{\hgxcmd{mq}{qrefresh}---update the topmost applied patch} - -The \hgxcmd{mq}{qrefresh} command updates the topmost applied patch. It -modifies the patch, removes the old changeset that represented the -patch, and creates a new changeset to represent the modified patch. - -The \hgxcmd{mq}{qrefresh} command looks for the following modifications: -\begin{itemize} -\item Changes to the commit message, i.e.~the text before the first - diff header in the patch file, are reflected in the new changeset - that represents the patch. -\item Modifications to tracked files in the working directory are - added to the patch. -\item Changes to the files tracked using \hgcmd{add}, \hgcmd{copy}, - \hgcmd{remove}, or \hgcmd{rename}. Added files and copy and rename - destinations are added to the patch, while removed files and rename - sources are removed. -\end{itemize} - -Even if \hgxcmd{mq}{qrefresh} detects no changes, it still recreates the -changeset that represents the patch. This causes the identity of the -changeset to differ from the previous changeset that identified the -patch. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qrefresh}{-e}] Modify the commit and patch description, - using the preferred text editor. -\item[\hgxopt{mq}{qrefresh}{-m}] Modify the commit message and patch - description, using the given text. -\item[\hgxopt{mq}{qrefresh}{-l}] Modify the commit message and patch - description, using text from the given file. -\end{itemize} - -\subsection{\hgxcmd{mq}{qrename}---rename a patch} - -The \hgxcmd{mq}{qrename} command renames a patch, and changes the entry for -the patch in the \sfilename{series} file. - -With a single argument, \hgxcmd{mq}{qrename} renames the topmost applied -patch. With two arguments, it renames its first argument to its -second. - -\subsection{\hgxcmd{mq}{qrestore}---restore saved queue state} - -XXX No idea what this does. - -\subsection{\hgxcmd{mq}{qsave}---save current queue state} - -XXX Likewise. - -\subsection{\hgxcmd{mq}{qseries}---print the entire patch series} - -The \hgxcmd{mq}{qseries} command prints the entire patch series from the -\sfilename{series} file. It prints only patch names, not empty lines -or comments. It prints in order from first to be applied to last. - -\subsection{\hgxcmd{mq}{qtop}---print the name of the current patch} - -The \hgxcmd{mq}{qtop} prints the name of the topmost currently applied -patch. - -\subsection{\hgxcmd{mq}{qunapplied}---print patches not yet applied} - -The \hgxcmd{mq}{qunapplied} command prints the names of patches from the -\sfilename{series} file that are not yet applied. It prints them in -order from the next patch that will be pushed to the last. - -\subsection{\hgcmd{strip}---remove a revision and descendants} - -The \hgcmd{strip} command removes a revision, and all of its -descendants, from the repository. It undoes the effects of the -removed revisions from the repository, and updates the working -directory to the first parent of the removed revision. - -The \hgcmd{strip} command saves a backup of the removed changesets in -a bundle, so that they can be reapplied if removed in error. - -Options: -\begin{itemize} -\item[\hgopt{strip}{-b}] Save unrelated changesets that are intermixed - with the stripped changesets in the backup bundle. -\item[\hgopt{strip}{-f}] If a branch has multiple heads, remove all - heads. XXX This should be renamed, and use \texttt{-f} to strip revs - when there are pending changes. -\item[\hgopt{strip}{-n}] Do not save a backup bundle. -\end{itemize} - -\section{MQ file reference} - -\subsection{The \sfilename{series} file} - -The \sfilename{series} file contains a list of the names of all -patches that MQ can apply. It is represented as a list of names, with -one name saved per line. Leading and trailing white space in each -line are ignored. - -Lines may contain comments. A comment begins with the ``\texttt{\#}'' -character, and extends to the end of the line. Empty lines, and lines -that contain only comments, are ignored. - -You will often need to edit the \sfilename{series} file by hand, hence -the support for comments and empty lines noted above. For example, -you can comment out a patch temporarily, and \hgxcmd{mq}{qpush} will skip -over that patch when applying patches. You can also change the order -in which patches are applied by reordering their entries in the -\sfilename{series} file. - -Placing the \sfilename{series} file under revision control is also -supported; it is a good idea to place all of the patches that it -refers to under revision control, as well. If you create a patch -directory using the \hgxopt{mq}{qinit}{-c} option to \hgxcmd{mq}{qinit}, this -will be done for you automatically. - -\subsection{The \sfilename{status} file} - -The \sfilename{status} file contains the names and changeset hashes of -all patches that MQ currently has applied. Unlike the -\sfilename{series} file, this file is not intended for editing. You -should not place this file under revision control, or modify it in any -way. It is used by MQ strictly for internal book-keeping. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/mq-stack.svg --- a/fr/mq-stack.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - prevent-compiler-reorder.patch - - namespace-cleanup.patch - - powerpc-port-fixes.patch - - report-devinfo-correctly.patch - { - { - present in series,but not applied - patches applied,changesets present - topmostapplied patch - 201ad3209902 - 126b84e593ae - a655daf15409 - e50d59aaea3a - - forbid-illegal-params.patch - - fix-memory-leak.patch - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/mq.tex --- a/fr/mq.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1043 +0,0 @@ -\chapter{Managing change with Mercurial Queues} -\label{chap:mq} - -\section{The patch management problem} -\label{sec:mq:patch-mgmt} - -Here is a common scenario: you need to install a software package from -source, but you find a bug that you must fix in the source before you -can start using the package. You make your changes, forget about the -package for a while, and a few months later you need to upgrade to a -newer version of the package. If the newer version of the package -still has the bug, you must extract your fix from the older source -tree and apply it against the newer version. This is a tedious task, -and it's easy to make mistakes. - -This is a simple case of the ``patch management'' problem. You have -an ``upstream'' source tree that you can't change; you need to make -some local changes on top of the upstream tree; and you'd like to be -able to keep those changes separate, so that you can apply them to -newer versions of the upstream source. - -The patch management problem arises in many situations. Probably the -most visible is that a user of an open source software project will -contribute a bug fix or new feature to the project's maintainers in the -form of a patch. - -Distributors of operating systems that include open source software -often need to make changes to the packages they distribute so that -they will build properly in their environments. - -When you have few changes to maintain, it is easy to manage a single -patch using the standard \command{diff} and \command{patch} programs -(see section~\ref{sec:mq:patch} for a discussion of these tools). -Once the number of changes grows, it starts to make sense to maintain -patches as discrete ``chunks of work,'' so that for example a single -patch will contain only one bug fix (the patch might modify several -files, but it's doing ``only one thing''), and you may have a number -of such patches for different bugs you need fixed and local changes -you require. In this situation, if you submit a bug fix patch to the -upstream maintainers of a package and they include your fix in a -subsequent release, you can simply drop that single patch when you're -updating to the newer release. - -Maintaining a single patch against an upstream tree is a little -tedious and error-prone, but not difficult. However, the complexity -of the problem grows rapidly as the number of patches you have to -maintain increases. With more than a tiny number of patches in hand, -understanding which ones you have applied and maintaining them moves -from messy to overwhelming. - -Fortunately, Mercurial includes a powerful extension, Mercurial Queues -(or simply ``MQ''), that massively simplifies the patch management -problem. - -\section{The prehistory of Mercurial Queues} -\label{sec:mq:history} - -During the late 1990s, several Linux kernel developers started to -maintain ``patch series'' that modified the behaviour of the Linux -kernel. Some of these series were focused on stability, some on -feature coverage, and others were more speculative. - -The sizes of these patch series grew rapidly. In 2002, Andrew Morton -published some shell scripts he had been using to automate the task of -managing his patch queues. Andrew was successfully using these -scripts to manage hundreds (sometimes thousands) of patches on top of -the Linux kernel. - -\subsection{A patchwork quilt} -\label{sec:mq:quilt} - -In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the -approach of Andrew's scripts and published a tool called ``patchwork -quilt''~\cite{web:quilt}, or simply ``quilt'' -(see~\cite{gruenbacher:2005} for a paper describing it). Because -quilt substantially automated patch management, it rapidly gained a -large following among open source software developers. - -Quilt manages a \emph{stack of patches} on top of a directory tree. -To begin, you tell quilt to manage a directory tree, and tell it which -files you want to manage; it stores away the names and contents of -those files. To fix a bug, you create a new patch (using a single -command), edit the files you need to fix, then ``refresh'' the patch. - -The refresh step causes quilt to scan the directory tree; it updates -the patch with all of the changes you have made. You can create -another patch on top of the first, which will track the changes -required to modify the tree from ``tree with one patch applied'' to -``tree with two patches applied''. - -You can \emph{change} which patches are applied to the tree. If you -``pop'' a patch, the changes made by that patch will vanish from the -directory tree. Quilt remembers which patches you have popped, -though, so you can ``push'' a popped patch again, and the directory -tree will be restored to contain the modifications in the patch. Most -importantly, you can run the ``refresh'' command at any time, and the -topmost applied patch will be updated. This means that you can, at -any time, change both which patches are applied and what -modifications those patches make. - -Quilt knows nothing about revision control tools, so it works equally -well on top of an unpacked tarball or a Subversion working copy. - -\subsection{From patchwork quilt to Mercurial Queues} -\label{sec:mq:quilt-mq} - -In mid-2005, Chris Mason took the features of quilt and wrote an -extension that he called Mercurial Queues, which added quilt-like -behaviour to Mercurial. - -The key difference between quilt and MQ is that quilt knows nothing -about revision control systems, while MQ is \emph{integrated} into -Mercurial. Each patch that you push is represented as a Mercurial -changeset. Pop a patch, and the changeset goes away. - -Because quilt does not care about revision control tools, it is still -a tremendously useful piece of software to know about for situations -where you cannot use Mercurial and MQ. - -\section{The huge advantage of MQ} - -I cannot overstate the value that MQ offers through the unification of -patches and revision control. - -A major reason that patches have persisted in the free software and -open source world---in spite of the availability of increasingly -capable revision control tools over the years---is the \emph{agility} -they offer. - -Traditional revision control tools make a permanent, irreversible -record of everything that you do. While this has great value, it's -also somewhat stifling. If you want to perform a wild-eyed -experiment, you have to be careful in how you go about it, or you risk -leaving unneeded---or worse, misleading or destabilising---traces of -your missteps and errors in the permanent revision record. - -By contrast, MQ's marriage of distributed revision control with -patches makes it much easier to isolate your work. Your patches live -on top of normal revision history, and you can make them disappear or -reappear at will. If you don't like a patch, you can drop it. If a -patch isn't quite as you want it to be, simply fix it---as many times -as you need to, until you have refined it into the form you desire. - -As an example, the integration of patches with revision control makes -understanding patches and debugging their effects---and their -interplay with the code they're based on---\emph{enormously} easier. -Since every applied patch has an associated changeset, you can use -\hgcmdargs{log}{\emph{filename}} to see which changesets and patches -affected a file. You can use the \hgext{bisect} command to -binary-search through all changesets and applied patches to see where -a bug got introduced or fixed. You can use the \hgcmd{annotate} -command to see which changeset or patch modified a particular line of -a source file. And so on. - -\section{Understanding patches} -\label{sec:mq:patch} - -Because MQ doesn't hide its patch-oriented nature, it is helpful to -understand what patches are, and a little about the tools that work -with them. - -The traditional Unix \command{diff} command compares two files, and -prints a list of differences between them. The \command{patch} command -understands these differences as \emph{modifications} to make to a -file. Take a look at figure~\ref{ex:mq:diff} for a simple example of -these commands in action. - -\begin{figure}[ht] - \interaction{mq.dodiff.diff} - \caption{Simple uses of the \command{diff} and \command{patch} commands} - \label{ex:mq:diff} -\end{figure} - -The type of file that \command{diff} generates (and \command{patch} -takes as input) is called a ``patch'' or a ``diff''; there is no -difference between a patch and a diff. (We'll use the term ``patch'', -since it's more commonly used.) - -A patch file can start with arbitrary text; the \command{patch} -command ignores this text, but MQ uses it as the commit message when -creating changesets. To find the beginning of the patch content, -\command{patch} searches for the first line that starts with the -string ``\texttt{diff~-}''. - -MQ works with \emph{unified} diffs (\command{patch} can accept several -other diff formats, but MQ doesn't). A unified diff contains two -kinds of header. The \emph{file header} describes the file being -modified; it contains the name of the file to modify. When -\command{patch} sees a new file header, it looks for a file with that -name to start modifying. - -After the file header comes a series of \emph{hunks}. Each hunk -starts with a header; this identifies the range of line numbers within -the file that the hunk should modify. Following the header, a hunk -starts and ends with a few (usually three) lines of text from the -unmodified file; these are called the \emph{context} for the hunk. If -there's only a small amount of context between successive hunks, -\command{diff} doesn't print a new hunk header; it just runs the hunks -together, with a few lines of context between modifications. - -Each line of context begins with a space character. Within the hunk, -a line that begins with ``\texttt{-}'' means ``remove this line,'' -while a line that begins with ``\texttt{+}'' means ``insert this -line.'' For example, a line that is modified is represented by one -deletion and one insertion. - -We will return to some of the more subtle aspects of patches later (in -section~\ref{sec:mq:adv-patch}), but you should have enough information -now to use MQ. - -\section{Getting started with Mercurial Queues} -\label{sec:mq:start} - -Because MQ is implemented as an extension, you must explicitly enable -before you can use it. (You don't need to download anything; MQ ships -with the standard Mercurial distribution.) To enable MQ, edit your -\tildefile{.hgrc} file, and add the lines in figure~\ref{ex:mq:config}. - -\begin{figure}[ht] - \begin{codesample4} - [extensions] - hgext.mq = - \end{codesample4} - \label{ex:mq:config} - \caption{Contents to add to \tildefile{.hgrc} to enable the MQ extension} -\end{figure} - -Once the extension is enabled, it will make a number of new commands -available. To verify that the extension is working, you can use -\hgcmd{help} to see if the \hgxcmd{mq}{qinit} command is now available; see -the example in figure~\ref{ex:mq:enabled}. - -\begin{figure}[ht] - \interaction{mq.qinit-help.help} - \caption{How to verify that MQ is enabled} - \label{ex:mq:enabled} -\end{figure} - -You can use MQ with \emph{any} Mercurial repository, and its commands -only operate within that repository. To get started, simply prepare -the repository using the \hgxcmd{mq}{qinit} command (see -figure~\ref{ex:mq:qinit}). This command creates an empty directory -called \sdirname{.hg/patches}, where MQ will keep its metadata. As -with many Mercurial commands, the \hgxcmd{mq}{qinit} command prints nothing -if it succeeds. - -\begin{figure}[ht] - \interaction{mq.tutorial.qinit} - \caption{Preparing a repository for use with MQ} - \label{ex:mq:qinit} -\end{figure} - -\begin{figure}[ht] - \interaction{mq.tutorial.qnew} - \caption{Creating a new patch} - \label{ex:mq:qnew} -\end{figure} - -\subsection{Creating a new patch} - -To begin work on a new patch, use the \hgxcmd{mq}{qnew} command. This -command takes one argument, the name of the patch to create. MQ will -use this as the name of an actual file in the \sdirname{.hg/patches} -directory, as you can see in figure~\ref{ex:mq:qnew}. - -Also newly present in the \sdirname{.hg/patches} directory are two -other files, \sfilename{series} and \sfilename{status}. The -\sfilename{series} file lists all of the patches that MQ knows about -for this repository, with one patch per line. Mercurial uses the -\sfilename{status} file for internal book-keeping; it tracks all of the -patches that MQ has \emph{applied} in this repository. - -\begin{note} - You may sometimes want to edit the \sfilename{series} file by hand; - for example, to change the sequence in which some patches are - applied. However, manually editing the \sfilename{status} file is - almost always a bad idea, as it's easy to corrupt MQ's idea of what - is happening. -\end{note} - -Once you have created your new patch, you can edit files in the -working directory as you usually would. All of the normal Mercurial -commands, such as \hgcmd{diff} and \hgcmd{annotate}, work exactly as -they did before. - -\subsection{Refreshing a patch} - -When you reach a point where you want to save your work, use the -\hgxcmd{mq}{qrefresh} command (figure~\ref{ex:mq:qnew}) to update the patch -you are working on. This command folds the changes you have made in -the working directory into your patch, and updates its corresponding -changeset to contain those changes. - -\begin{figure}[ht] - \interaction{mq.tutorial.qrefresh} - \caption{Refreshing a patch} - \label{ex:mq:qrefresh} -\end{figure} - -You can run \hgxcmd{mq}{qrefresh} as often as you like, so it's a good way -to ``checkpoint'' your work. Refresh your patch at an opportune -time; try an experiment; and if the experiment doesn't work out, -\hgcmd{revert} your modifications back to the last time you refreshed. - -\begin{figure}[ht] - \interaction{mq.tutorial.qrefresh2} - \caption{Refresh a patch many times to accumulate changes} - \label{ex:mq:qrefresh2} -\end{figure} - -\subsection{Stacking and tracking patches} - -Once you have finished working on a patch, or need to work on another, -you can use the \hgxcmd{mq}{qnew} command again to create a new patch. -Mercurial will apply this patch on top of your existing patch. See -figure~\ref{ex:mq:qnew2} for an example. Notice that the patch -contains the changes in our prior patch as part of its context (you -can see this more clearly in the output of \hgcmd{annotate}). - -\begin{figure}[ht] - \interaction{mq.tutorial.qnew2} - \caption{Stacking a second patch on top of the first} - \label{ex:mq:qnew2} -\end{figure} - -So far, with the exception of \hgxcmd{mq}{qnew} and \hgxcmd{mq}{qrefresh}, we've -been careful to only use regular Mercurial commands. However, MQ -provides many commands that are easier to use when you are thinking -about patches, as illustrated in figure~\ref{ex:mq:qseries}: - -\begin{itemize} -\item The \hgxcmd{mq}{qseries} command lists every patch that MQ knows - about in this repository, from oldest to newest (most recently - \emph{created}). -\item The \hgxcmd{mq}{qapplied} command lists every patch that MQ has - \emph{applied} in this repository, again from oldest to newest (most - recently applied). -\end{itemize} - -\begin{figure}[ht] - \interaction{mq.tutorial.qseries} - \caption{Understanding the patch stack with \hgxcmd{mq}{qseries} and - \hgxcmd{mq}{qapplied}} - \label{ex:mq:qseries} -\end{figure} - -\subsection{Manipulating the patch stack} - -The previous discussion implied that there must be a difference -between ``known'' and ``applied'' patches, and there is. MQ can -manage a patch without it being applied in the repository. - -An \emph{applied} patch has a corresponding changeset in the -repository, and the effects of the patch and changeset are visible in -the working directory. You can undo the application of a patch using -the \hgxcmd{mq}{qpop} command. MQ still \emph{knows about}, or manages, a -popped patch, but the patch no longer has a corresponding changeset in -the repository, and the working directory does not contain the changes -made by the patch. Figure~\ref{fig:mq:stack} illustrates the -difference between applied and tracked patches. - -\begin{figure}[ht] - \centering - \grafix{mq-stack} - \caption{Applied and unapplied patches in the MQ patch stack} - \label{fig:mq:stack} -\end{figure} - -You can reapply an unapplied, or popped, patch using the \hgxcmd{mq}{qpush} -command. This creates a new changeset to correspond to the patch, and -the patch's changes once again become present in the working -directory. See figure~\ref{ex:mq:qpop} for examples of \hgxcmd{mq}{qpop} -and \hgxcmd{mq}{qpush} in action. Notice that once we have popped a patch -or two patches, the output of \hgxcmd{mq}{qseries} remains the same, while -that of \hgxcmd{mq}{qapplied} has changed. - -\begin{figure}[ht] - \interaction{mq.tutorial.qpop} - \caption{Modifying the stack of applied patches} - \label{ex:mq:qpop} -\end{figure} - -\subsection{Pushing and popping many patches} - -While \hgxcmd{mq}{qpush} and \hgxcmd{mq}{qpop} each operate on a single patch at -a time by default, you can push and pop many patches in one go. The -\hgxopt{mq}{qpush}{-a} option to \hgxcmd{mq}{qpush} causes it to push all -unapplied patches, while the \hgxopt{mq}{qpop}{-a} option to \hgxcmd{mq}{qpop} -causes it to pop all applied patches. (For some more ways to push and -pop many patches, see section~\ref{sec:mq:perf} below.) - -\begin{figure}[ht] - \interaction{mq.tutorial.qpush-a} - \caption{Pushing all unapplied patches} - \label{ex:mq:qpush-a} -\end{figure} - -\subsection{Safety checks, and overriding them} - -Several MQ commands check the working directory before they do -anything, and fail if they find any modifications. They do this to -ensure that you won't lose any changes that you have made, but not yet -incorporated into a patch. Figure~\ref{ex:mq:add} illustrates this; -the \hgxcmd{mq}{qnew} command will not create a new patch if there are -outstanding changes, caused in this case by the \hgcmd{add} of -\filename{file3}. - -\begin{figure}[ht] - \interaction{mq.tutorial.add} - \caption{Forcibly creating a patch} - \label{ex:mq:add} -\end{figure} - -Commands that check the working directory all take an ``I know what -I'm doing'' option, which is always named \option{-f}. The exact -meaning of \option{-f} depends on the command. For example, -\hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} will incorporate any outstanding -changes into the new patch it creates, but -\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} will revert modifications to any -files affected by the patch that it is popping. Be sure to read the -documentation for a command's \option{-f} option before you use it! - -\subsection{Working on several patches at once} - -The \hgxcmd{mq}{qrefresh} command always refreshes the \emph{topmost} -applied patch. This means that you can suspend work on one patch (by -refreshing it), pop or push to make a different patch the top, and -work on \emph{that} patch for a while. - -Here's an example that illustrates how you can use this ability. -Let's say you're developing a new feature as two patches. The first -is a change to the core of your software, and the second---layered on -top of the first---changes the user interface to use the code you just -added to the core. If you notice a bug in the core while you're -working on the UI patch, it's easy to fix the core. Simply -\hgxcmd{mq}{qrefresh} the UI patch to save your in-progress changes, and -\hgxcmd{mq}{qpop} down to the core patch. Fix the core bug, -\hgxcmd{mq}{qrefresh} the core patch, and \hgxcmd{mq}{qpush} back to the UI -patch to continue where you left off. - -\section{More about patches} -\label{sec:mq:adv-patch} - -MQ uses the GNU \command{patch} command to apply patches, so it's -helpful to know a few more detailed aspects of how \command{patch} -works, and about patches themselves. - -\subsection{The strip count} - -If you look at the file headers in a patch, you will notice that the -pathnames usually have an extra component on the front that isn't -present in the actual path name. This is a holdover from the way that -people used to generate patches (people still do this, but it's -somewhat rare with modern revision control tools). - -Alice would unpack a tarball, edit her files, then decide that she -wanted to create a patch. So she'd rename her working directory, -unpack the tarball again (hence the need for the rename), and use the -\cmdopt{diff}{-r} and \cmdopt{diff}{-N} options to \command{diff} to -recursively generate a patch between the unmodified directory and the -modified one. The result would be that the name of the unmodified -directory would be at the front of the left-hand path in every file -header, and the name of the modified directory would be at the front -of the right-hand path. - -Since someone receiving a patch from the Alices of the net would be -unlikely to have unmodified and modified directories with exactly the -same names, the \command{patch} command has a \cmdopt{patch}{-p} -option that indicates the number of leading path name components to -strip when trying to apply a patch. This number is called the -\emph{strip count}. - -An option of ``\texttt{-p1}'' means ``use a strip count of one''. If -\command{patch} sees a file name \filename{foo/bar/baz} in a file -header, it will strip \filename{foo} and try to patch a file named -\filename{bar/baz}. (Strictly speaking, the strip count refers to the -number of \emph{path separators} (and the components that go with them -) to strip. A strip count of one will turn \filename{foo/bar} into -\filename{bar}, but \filename{/foo/bar} (notice the extra leading -slash) into \filename{foo/bar}.) - -The ``standard'' strip count for patches is one; almost all patches -contain one leading path name component that needs to be stripped. -Mercurial's \hgcmd{diff} command generates path names in this form, -and the \hgcmd{import} command and MQ expect patches to have a strip -count of one. - -If you receive a patch from someone that you want to add to your patch -queue, and the patch needs a strip count other than one, you cannot -just \hgxcmd{mq}{qimport} the patch, because \hgxcmd{mq}{qimport} does not yet -have a \texttt{-p} option (see~\bug{311}). Your best bet is to -\hgxcmd{mq}{qnew} a patch of your own, then use \cmdargs{patch}{-p\emph{N}} -to apply their patch, followed by \hgcmd{addremove} to pick up any -files added or removed by the patch, followed by \hgxcmd{mq}{qrefresh}. -This complexity may become unnecessary; see~\bug{311} for details. -\subsection{Strategies for applying a patch} - -When \command{patch} applies a hunk, it tries a handful of -successively less accurate strategies to try to make the hunk apply. -This falling-back technique often makes it possible to take a patch -that was generated against an old version of a file, and apply it -against a newer version of that file. - -First, \command{patch} tries an exact match, where the line numbers, -the context, and the text to be modified must apply exactly. If it -cannot make an exact match, it tries to find an exact match for the -context, without honouring the line numbering information. If this -succeeds, it prints a line of output saying that the hunk was applied, -but at some \emph{offset} from the original line number. - -If a context-only match fails, \command{patch} removes the first and -last lines of the context, and tries a \emph{reduced} context-only -match. If the hunk with reduced context succeeds, it prints a message -saying that it applied the hunk with a \emph{fuzz factor} (the number -after the fuzz factor indicates how many lines of context -\command{patch} had to trim before the patch applied). - -When neither of these techniques works, \command{patch} prints a -message saying that the hunk in question was rejected. It saves -rejected hunks (also simply called ``rejects'') to a file with the -same name, and an added \sfilename{.rej} extension. It also saves an -unmodified copy of the file with a \sfilename{.orig} extension; the -copy of the file without any extensions will contain any changes made -by hunks that \emph{did} apply cleanly. If you have a patch that -modifies \filename{foo} with six hunks, and one of them fails to -apply, you will have: an unmodified \filename{foo.orig}, a -\filename{foo.rej} containing one hunk, and \filename{foo}, containing -the changes made by the five successful hunks. - -\subsection{Some quirks of patch representation} - -There are a few useful things to know about how \command{patch} works -with files. -\begin{itemize} -\item This should already be obvious, but \command{patch} cannot - handle binary files. -\item Neither does it care about the executable bit; it creates new - files as readable, but not executable. -\item \command{patch} treats the removal of a file as a diff between - the file to be removed and the empty file. So your idea of ``I - deleted this file'' looks like ``every line of this file was - deleted'' in a patch. -\item It treats the addition of a file as a diff between the empty - file and the file to be added. So in a patch, your idea of ``I - added this file'' looks like ``every line of this file was added''. -\item It treats a renamed file as the removal of the old name, and the - addition of the new name. This means that renamed files have a big - footprint in patches. (Note also that Mercurial does not currently - try to infer when files have been renamed or copied in a patch.) -\item \command{patch} cannot represent empty files, so you cannot use - a patch to represent the notion ``I added this empty file to the - tree''. -\end{itemize} -\subsection{Beware the fuzz} - -While applying a hunk at an offset, or with a fuzz factor, will often -be completely successful, these inexact techniques naturally leave -open the possibility of corrupting the patched file. The most common -cases typically involve applying a patch twice, or at an incorrect -location in the file. If \command{patch} or \hgxcmd{mq}{qpush} ever -mentions an offset or fuzz factor, you should make sure that the -modified files are correct afterwards. - -It's often a good idea to refresh a patch that has applied with an -offset or fuzz factor; refreshing the patch generates new context -information that will make it apply cleanly. I say ``often,'' not -``always,'' because sometimes refreshing a patch will make it fail to -apply against a different revision of the underlying files. In some -cases, such as when you're maintaining a patch that must sit on top of -multiple versions of a source tree, it's acceptable to have a patch -apply with some fuzz, provided you've verified the results of the -patching process in such cases. - -\subsection{Handling rejection} - -If \hgxcmd{mq}{qpush} fails to apply a patch, it will print an error -message and exit. If it has left \sfilename{.rej} files behind, it is -usually best to fix up the rejected hunks before you push more patches -or do any further work. - -If your patch \emph{used to} apply cleanly, and no longer does because -you've changed the underlying code that your patches are based on, -Mercurial Queues can help; see section~\ref{sec:mq:merge} for details. - -Unfortunately, there aren't any great techniques for dealing with -rejected hunks. Most often, you'll need to view the \sfilename{.rej} -file and edit the target file, applying the rejected hunks by hand. - -If you're feeling adventurous, Neil Brown, a Linux kernel hacker, -wrote a tool called \command{wiggle}~\cite{web:wiggle}, which is more -vigorous than \command{patch} in its attempts to make a patch apply. - -Another Linux kernel hacker, Chris Mason (the author of Mercurial -Queues), wrote a similar tool called -\command{mpatch}~\cite{web:mpatch}, which takes a simple approach to -automating the application of hunks rejected by \command{patch}. The -\command{mpatch} command can help with four common reasons that a hunk -may be rejected: - -\begin{itemize} -\item The context in the middle of a hunk has changed. -\item A hunk is missing some context at the beginning or end. -\item A large hunk might apply better---either entirely or in - part---if it was broken up into smaller hunks. -\item A hunk removes lines with slightly different content than those - currently present in the file. -\end{itemize} - -If you use \command{wiggle} or \command{mpatch}, you should be doubly -careful to check your results when you're done. In fact, -\command{mpatch} enforces this method of double-checking the tool's -output, by automatically dropping you into a merge program when it has -done its job, so that you can verify its work and finish off any -remaining merges. - -\section{Getting the best performance out of MQ} -\label{sec:mq:perf} - -MQ is very efficient at handling a large number of patches. I ran -some performance experiments in mid-2006 for a talk that I gave at the -2006 EuroPython conference~\cite{web:europython}. I used as my data -set the Linux 2.6.17-mm1 patch series, which consists of 1,738 -patches. I applied these on top of a Linux kernel repository -containing all 27,472 revisions between Linux 2.6.12-rc2 and Linux -2.6.17. - -On my old, slow laptop, I was able to -\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} all 1,738 patches in 3.5 minutes, -and \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} them all in 30 seconds. (On a -newer laptop, the time to push all patches dropped to two minutes.) I -could \hgxcmd{mq}{qrefresh} one of the biggest patches (which made 22,779 -lines of changes to 287 files) in 6.6 seconds. - -Clearly, MQ is well suited to working in large trees, but there are a -few tricks you can use to get the best performance of it. - -First of all, try to ``batch'' operations together. Every time you -run \hgxcmd{mq}{qpush} or \hgxcmd{mq}{qpop}, these commands scan the working -directory once to make sure you haven't made some changes and then -forgotten to run \hgxcmd{mq}{qrefresh}. On a small tree, the time that -this scan takes is unnoticeable. However, on a medium-sized tree -(containing tens of thousands of files), it can take a second or more. - -The \hgxcmd{mq}{qpush} and \hgxcmd{mq}{qpop} commands allow you to push and pop -multiple patches at a time. You can identify the ``destination -patch'' that you want to end up at. When you \hgxcmd{mq}{qpush} with a -destination specified, it will push patches until that patch is at the -top of the applied stack. When you \hgxcmd{mq}{qpop} to a destination, MQ -will pop patches until the destination patch is at the top. - -You can identify a destination patch using either the name of the -patch, or by number. If you use numeric addressing, patches are -counted from zero; this means that the first patch is zero, the second -is one, and so on. - -\section{Updating your patches when the underlying code changes} -\label{sec:mq:merge} - -It's common to have a stack of patches on top of an underlying -repository that you don't modify directly. If you're working on -changes to third-party code, or on a feature that is taking longer to -develop than the rate of change of the code beneath, you will often -need to sync up with the underlying code, and fix up any hunks in your -patches that no longer apply. This is called \emph{rebasing} your -patch series. - -The simplest way to do this is to \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} -your patches, then \hgcmd{pull} changes into the underlying -repository, and finally \hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} your -patches again. MQ will stop pushing any time it runs across a patch -that fails to apply during conflicts, allowing you to fix your -conflicts, \hgxcmd{mq}{qrefresh} the affected patch, and continue pushing -until you have fixed your entire stack. - -This approach is easy to use and works well if you don't expect -changes to the underlying code to affect how well your patches apply. -If your patch stack touches code that is modified frequently or -invasively in the underlying repository, however, fixing up rejected -hunks by hand quickly becomes tiresome. - -It's possible to partially automate the rebasing process. If your -patches apply cleanly against some revision of the underlying repo, MQ -can use this information to help you to resolve conflicts between your -patches and a different revision. - -The process is a little involved. -\begin{enumerate} -\item To begin, \hgcmdargs{qpush}{-a} all of your patches on top of - the revision where you know that they apply cleanly. -\item Save a backup copy of your patch directory using - \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}}. This prints - the name of the directory that it has saved the patches in. It will - save the patches to a directory called - \sdirname{.hg/patches.\emph{N}}, where \texttt{\emph{N}} is a small - integer. It also commits a ``save changeset'' on top of your - applied patches; this is for internal book-keeping, and records the - states of the \sfilename{series} and \sfilename{status} files. -\item Use \hgcmd{pull} to bring new changes into the underlying - repository. (Don't run \hgcmdargs{pull}{-u}; see below for why.) -\item Update to the new tip revision, using - \hgcmdargs{update}{\hgopt{update}{-C}} to override the patches you - have pushed. -\item Merge all patches using \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} - \hgxopt{mq}{qpush}{-a}}. The \hgxopt{mq}{qpush}{-m} option to \hgxcmd{mq}{qpush} - tells MQ to perform a three-way merge if the patch fails to apply. -\end{enumerate} - -During the \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}}, each patch in the -\sfilename{series} file is applied normally. If a patch applies with -fuzz or rejects, MQ looks at the queue you \hgxcmd{mq}{qsave}d, and -performs a three-way merge with the corresponding changeset. This -merge uses Mercurial's normal merge machinery, so it may pop up a GUI -merge tool to help you to resolve problems. - -When you finish resolving the effects of a patch, MQ refreshes your -patch based on the result of the merge. - -At the end of this process, your repository will have one extra head -from the old patch queue, and a copy of the old patch queue will be in -\sdirname{.hg/patches.\emph{N}}. You can remove the extra head using -\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} -or \hgcmd{strip}. You can delete \sdirname{.hg/patches.\emph{N}} once -you are sure that you no longer need it as a backup. - -\section{Identifying patches} - -MQ commands that work with patches let you refer to a patch either by -using its name or by a number. By name is obvious enough; pass the -name \filename{foo.patch} to \hgxcmd{mq}{qpush}, for example, and it will -push patches until \filename{foo.patch} is applied. - -As a shortcut, you can refer to a patch using both a name and a -numeric offset; \texttt{foo.patch-2} means ``two patches before -\texttt{foo.patch}'', while \texttt{bar.patch+4} means ``four patches -after \texttt{bar.patch}''. - -Referring to a patch by index isn't much different. The first patch -printed in the output of \hgxcmd{mq}{qseries} is patch zero (yes, it's one -of those start-at-zero counting systems); the second is patch one; and -so on. - -MQ also makes it easy to work with patches when you are using normal -Mercurial commands. Every command that accepts a changeset ID will -also accept the name of an applied patch. MQ augments the tags -normally in the repository with an eponymous one for each applied -patch. In addition, the special tags \index{tags!special tag - names!\texttt{qbase}}\texttt{qbase} and \index{tags!special tag - names!\texttt{qtip}}\texttt{qtip} identify the ``bottom-most'' and -topmost applied patches, respectively. - -These additions to Mercurial's normal tagging capabilities make -dealing with patches even more of a breeze. -\begin{itemize} -\item Want to patchbomb a mailing list with your latest series of - changes? - \begin{codesample4} - hg email qbase:qtip - \end{codesample4} - (Don't know what ``patchbombing'' is? See - section~\ref{sec:hgext:patchbomb}.) -\item Need to see all of the patches since \texttt{foo.patch} that - have touched files in a subdirectory of your tree? - \begin{codesample4} - hg log -r foo.patch:qtip \emph{subdir} - \end{codesample4} -\end{itemize} - -Because MQ makes the names of patches available to the rest of -Mercurial through its normal internal tag machinery, you don't need to -type in the entire name of a patch when you want to identify it by -name. - -\begin{figure}[ht] - \interaction{mq.id.output} - \caption{Using MQ's tag features to work with patches} - \label{ex:mq:id} -\end{figure} - -Another nice consequence of representing patch names as tags is that -when you run the \hgcmd{log} command, it will display a patch's name -as a tag, simply as part of its normal output. This makes it easy to -visually distinguish applied patches from underlying ``normal'' -revisions. Figure~\ref{ex:mq:id} shows a few normal Mercurial -commands in use with applied patches. - -\section{Useful things to know about} - -There are a number of aspects of MQ usage that don't fit tidily into -sections of their own, but that are good to know. Here they are, in -one place. - -\begin{itemize} -\item Normally, when you \hgxcmd{mq}{qpop} a patch and \hgxcmd{mq}{qpush} it - again, the changeset that represents the patch after the pop/push - will have a \emph{different identity} than the changeset that - represented the hash beforehand. See - section~\ref{sec:mqref:cmd:qpush} for information as to why this is. -\item It's not a good idea to \hgcmd{merge} changes from another - branch with a patch changeset, at least if you want to maintain the - ``patchiness'' of that changeset and changesets below it on the - patch stack. If you try to do this, it will appear to succeed, but - MQ will become confused. -\end{itemize} - -\section{Managing patches in a repository} -\label{sec:mq:repo} - -Because MQ's \sdirname{.hg/patches} directory resides outside a -Mercurial repository's working directory, the ``underlying'' Mercurial -repository knows nothing about the management or presence of patches. - -This presents the interesting possibility of managing the contents of -the patch directory as a Mercurial repository in its own right. This -can be a useful way to work. For example, you can work on a patch for -a while, \hgxcmd{mq}{qrefresh} it, then \hgcmd{commit} the current state of -the patch. This lets you ``roll back'' to that version of the patch -later on. - -You can then share different versions of the same patch stack among -multiple underlying repositories. I use this when I am developing a -Linux kernel feature. I have a pristine copy of my kernel sources for -each of several CPU architectures, and a cloned repository under each -that contains the patches I am working on. When I want to test a -change on a different architecture, I push my current patches to the -patch repository associated with that kernel tree, pop and push all of -my patches, and build and test that kernel. - -Managing patches in a repository makes it possible for multiple -developers to work on the same patch series without colliding with -each other, all on top of an underlying source base that they may or -may not control. - -\subsection{MQ support for patch repositories} - -MQ helps you to work with the \sdirname{.hg/patches} directory as a -repository; when you prepare a repository for working with patches -using \hgxcmd{mq}{qinit}, you can pass the \hgxopt{mq}{qinit}{-c} option to -create the \sdirname{.hg/patches} directory as a Mercurial repository. - -\begin{note} - If you forget to use the \hgxopt{mq}{qinit}{-c} option, you can simply go - into the \sdirname{.hg/patches} directory at any time and run - \hgcmd{init}. Don't forget to add an entry for the - \sfilename{status} file to the \sfilename{.hgignore} file, though - - (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} does this for you - automatically); you \emph{really} don't want to manage the - \sfilename{status} file. -\end{note} - -As a convenience, if MQ notices that the \dirname{.hg/patches} -directory is a repository, it will automatically \hgcmd{add} every -patch that you create and import. - -MQ provides a shortcut command, \hgxcmd{mq}{qcommit}, that runs -\hgcmd{commit} in the \sdirname{.hg/patches} directory. This saves -some bothersome typing. - -Finally, as a convenience to manage the patch directory, you can -define the alias \command{mq} on Unix systems. For example, on Linux -systems using the \command{bash} shell, you can include the following -snippet in your \tildefile{.bashrc}. - -\begin{codesample2} - alias mq=`hg -R \$(hg root)/.hg/patches' -\end{codesample2} - -You can then issue commands of the form \cmdargs{mq}{pull} from -the main repository. - -\subsection{A few things to watch out for} - -MQ's support for working with a repository full of patches is limited -in a few small respects. - -MQ cannot automatically detect changes that you make to the patch -directory. If you \hgcmd{pull}, manually edit, or \hgcmd{update} -changes to patches or the \sfilename{series} file, you will have to -\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} and then -\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} in the underlying repository to -see those changes show up there. If you forget to do this, you can -confuse MQ's idea of which patches are applied. - -\section{Third party tools for working with patches} -\label{sec:mq:tools} - -Once you've been working with patches for a while, you'll find -yourself hungry for tools that will help you to understand and -manipulate the patches you're dealing with. - -The \command{diffstat} command~\cite{web:diffstat} generates a -histogram of the modifications made to each file in a patch. It -provides a good way to ``get a sense of'' a patch---which files it -affects, and how much change it introduces to each file and as a -whole. (I find that it's a good idea to use \command{diffstat}'s -\cmdopt{diffstat}{-p} option as a matter of course, as otherwise it -will try to do clever things with prefixes of file names that -inevitably confuse at least me.) - -\begin{figure}[ht] - \interaction{mq.tools.tools} - \caption{The \command{diffstat}, \command{filterdiff}, and \command{lsdiff} commands} - \label{ex:mq:tools} -\end{figure} - -The \package{patchutils} package~\cite{web:patchutils} is invaluable. -It provides a set of small utilities that follow the ``Unix -philosophy;'' each does one useful thing with a patch. The -\package{patchutils} command I use most is \command{filterdiff}, which -extracts subsets from a patch file. For example, given a patch that -modifies hundreds of files across dozens of directories, a single -invocation of \command{filterdiff} can generate a smaller patch that -only touches files whose names match a particular glob pattern. See -section~\ref{mq-collab:tips:interdiff} for another example. - -\section{Good ways to work with patches} - -Whether you are working on a patch series to submit to a free software -or open source project, or a series that you intend to treat as a -sequence of regular changesets when you're done, you can use some -simple techniques to keep your work well organised. - -Give your patches descriptive names. A good name for a patch might be -\filename{rework-device-alloc.patch}, because it will immediately give -you a hint what the purpose of the patch is. Long names shouldn't be -a problem; you won't be typing the names often, but you \emph{will} be -running commands like \hgxcmd{mq}{qapplied} and \hgxcmd{mq}{qtop} over and over. -Good naming becomes especially important when you have a number of -patches to work with, or if you are juggling a number of different -tasks and your patches only get a fraction of your attention. - -Be aware of what patch you're working on. Use the \hgxcmd{mq}{qtop} -command and skim over the text of your patches frequently---for -example, using \hgcmdargs{tip}{\hgopt{tip}{-p}})---to be sure of where -you stand. I have several times worked on and \hgxcmd{mq}{qrefresh}ed a -patch other than the one I intended, and it's often tricky to migrate -changes into the right patch after making them in the wrong one. - -For this reason, it is very much worth investing a little time to -learn how to use some of the third-party tools I described in -section~\ref{sec:mq:tools}, particularly \command{diffstat} and -\command{filterdiff}. The former will give you a quick idea of what -changes your patch is making, while the latter makes it easy to splice -hunks selectively out of one patch and into another. - -\section{MQ cookbook} - -\subsection{Manage ``trivial'' patches} - -Because the overhead of dropping files into a new Mercurial repository -is so low, it makes a lot of sense to manage patches this way even if -you simply want to make a few changes to a source tarball that you -downloaded. - -Begin by downloading and unpacking the source tarball, -and turning it into a Mercurial repository. -\interaction{mq.tarball.download} - -Continue by creating a patch stack and making your changes. -\interaction{mq.tarball.qinit} - -Let's say a few weeks or months pass, and your package author releases -a new version. First, bring their changes into the repository. -\interaction{mq.tarball.newsource} -The pipeline starting with \hgcmd{locate} above deletes all files in -the working directory, so that \hgcmd{commit}'s -\hgopt{commit}{--addremove} option can actually tell which files have -really been removed in the newer version of the source. - -Finally, you can apply your patches on top of the new tree. -\interaction{mq.tarball.repush} - -\subsection{Combining entire patches} -\label{sec:mq:combine} - -MQ provides a command, \hgxcmd{mq}{qfold} that lets you combine entire -patches. This ``folds'' the patches you name, in the order you name -them, into the topmost applied patch, and concatenates their -descriptions onto the end of its description. The patches that you -fold must be unapplied before you fold them. - -The order in which you fold patches matters. If your topmost applied -patch is \texttt{foo}, and you \hgxcmd{mq}{qfold} \texttt{bar} and -\texttt{quux} into it, you will end up with a patch that has the same -effect as if you applied first \texttt{foo}, then \texttt{bar}, -followed by \texttt{quux}. - -\subsection{Merging part of one patch into another} - -Merging \emph{part} of one patch into another is more difficult than -combining entire patches. - -If you want to move changes to entire files, you can use -\command{filterdiff}'s \cmdopt{filterdiff}{-i} and -\cmdopt{filterdiff}{-x} options to choose the modifications to snip -out of one patch, concatenating its output onto the end of the patch -you want to merge into. You usually won't need to modify the patch -you've merged the changes from. Instead, MQ will report some rejected -hunks when you \hgxcmd{mq}{qpush} it (from the hunks you moved into the -other patch), and you can simply \hgxcmd{mq}{qrefresh} the patch to drop -the duplicate hunks. - -If you have a patch that has multiple hunks modifying a file, and you -only want to move a few of those hunks, the job becomes more messy, -but you can still partly automate it. Use \cmdargs{lsdiff}{-nvv} to -print some metadata about the patch. -\interaction{mq.tools.lsdiff} - -This command prints three different kinds of number: -\begin{itemize} -\item (in the first column) a \emph{file number} to identify each file - modified in the patch; -\item (on the next line, indented) the line number within a modified - file where a hunk starts; and -\item (on the same line) a \emph{hunk number} to identify that hunk. -\end{itemize} - -You'll have to use some visual inspection, and reading of the patch, -to identify the file and hunk numbers you'll want, but you can then -pass them to to \command{filterdiff}'s \cmdopt{filterdiff}{--files} -and \cmdopt{filterdiff}{--hunks} options, to select exactly the file -and hunk you want to extract. - -Once you have this hunk, you can concatenate it onto the end of your -destination patch and continue with the remainder of -section~\ref{sec:mq:combine}. - -\section{Differences between quilt and MQ} - -If you are already familiar with quilt, MQ provides a similar command -set. There are a few differences in the way that it works. - -You will already have noticed that most quilt commands have MQ -counterparts that simply begin with a ``\texttt{q}''. The exceptions -are quilt's \texttt{add} and \texttt{remove} commands, the -counterparts for which are the normal Mercurial \hgcmd{add} and -\hgcmd{remove} commands. There is no MQ equivalent of the quilt -\texttt{edit} command. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/note.png Binary file fr/note.png has changed diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/preface.tex --- a/fr/preface.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -\chapter*{Préface} -\addcontentsline{toc}{chapter}{Préface} -\label{chap:preface} - -La gestion de source distribuée est encore un territoire peu exploré -et qui, par conséquent, a grandi très rapidement grâce à la seule -volonté de ses explorateurs. - -Je rédige un livre sur ce sujet car je crois que c'est un sujet -important qui mérite bien un guide du ``terrain''. J'ai choisi d'écrire -ce livre sur Mercurial car c'est l'outil le plus simple pour découvrir -ce nouveau monde et qu'en outre, il répond très bien au besoin de -réels environnements, là où d'autres outils de gestion de source n'y -parviennent pas. - -\section{Cet ouvrage est un travail en cours} - -Je publie ce livre tout en continuant à l'écrire, dans l'espoir qu'il -vous sera utile. J'espère aussi que les lecteurs pourront ainsi y contribuer -si ils le souhaitent. - -\section{À propros des exemples de ce livre} - -Ce livre a une approche particulière des exemples d'exécution. Ils sont -toujours ``dynamiques''---chacun est le résultat d'un script shell qui -exécute les commandes Mercurial que vous voyez. Chaque fois qu'une -image du livre est construite à partir des sources, tous les scripts d'exemple -sont exécutés automatiquement, et les résultats comparés à ceux attendus. - -Cette approche a l'avantage de garantir que les exemples sont toujours -justes ; ils montrent \emph{exactement} le comportement de la version de -Mercurial spécifiée dans la couverture de ce livre. Si je mets à jour cette -version, et que les commandes changent, la génération du livre échouera. - -Il y a un petit désavantage à cette approche, les dates et les -durées que vous verrez dans ces exemples ont tendances à être -``réduits'' de manière très différente d'une exécution manuelle. Un être humain -ne peut exécuter qu'une commande toutes les secondes, alors que mes scripts -automatisés en exécutent plusieurs en une seule seconde. - -Ainsi, en une seule seconde, plusieurs ``commits'' peuvent avoir lieu -au sein d'un exemple. Vous le constatez, entre autres, dans les -exemples sur \hgext{bisect}, dans la section~\ref{sec:undo:bisect}. - -En conséquence, quand vous lisez les exemples, n'accordez pas trop -d'importance aux dates et aux durées d'exécution des commandes. Mais -\emph{soyez sûr} que le comportement que vous voyez est cohérent et -reproductible. - -\section{Colophon---Cet ouvrage est libre} - -Ce livre est publié sous la licence ``Open Publication License'' -\footnote{Pour plus de renseignements : -\url{http://opencontent.org/openpub/}{Open Publication License} }, -et est construit uniquement à l'aide de logiciels libres. Il est mis -en forme avec \LaTex{}; et les illustrations sont réalisées avec -\href{http://www.inkscape.org/}{Inkscape}. - -L'ensemble des fichiers sources de cet ouvrage est publié dans un -dépot mercurial \url{http://hg.serpentine.com/mercurial/book}. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/revlog.svg --- a/fr/revlog.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - Second parent - 32bf9a5f22c0 - - - - Revision hash - 34b8b7a15ea1 - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - 000000000000 - - - - Second parent - 000000000000 - - - - - Revision hash - ff9dc8bc2a8b - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - 34b8b7a15ea1 - - - - Second parent - 000000000000 - - - - Revision hash - 1b67dc96f27a - - - - ... - Revision data (delta or snapshot) - - - - - - - - First parent - ff9dc8bc2a8b - - - - Second parent - 000000000000 - - - - Revision hash - 5b80c922ebdd - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - ecacb6b4c9fd - - - - Second parent - 000000000000 - - - - Revision hash - 32bf9a5f22c0 - - - - ... - Revision data (delta or snapshot) - - - - - - First parent - ff9dc8bc2a8b - - - - Second parent - 000000000000 - - - - Revision hash - ecacb6b4c9fd - - - - ... - Revision data (delta or snapshot) - - - - - - - Head revision(no children) - Merge revision(two parents) - Branches(two revisions,same parent) - - - First revision(both parents null) - - First parent - 5b80c922ebdd - - - - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/snapshot.svg --- a/fr/snapshot.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - - - - Index, rev 7 - - Revlog index (.i file) - Revlog data (.d file) - - - Snapshot, rev 4 - - Delta, rev 4 to 5 - - Delta, rev 5 to 6 - - Delta, rev 6 to 7 - - Delta, rev 2 to 3 - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/srcinstall.tex --- a/fr/srcinstall.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -\chapter{Installing Mercurial from source} -\label{chap:srcinstall} - -\section{On a Unix-like system} -\label{sec:srcinstall:unixlike} - -If you are using a Unix-like system that has a sufficiently recent -version of Python (2.3~or newer) available, it is easy to install -Mercurial from source. -\begin{enumerate} -\item Download a recent source tarball from - \url{http://www.selenic.com/mercurial/download}. -\item Unpack the tarball: - \begin{codesample4} - gzip -dc mercurial-\emph{version}.tar.gz | tar xf - - \end{codesample4} -\item Go into the source directory and run the installer script. This - will build Mercurial and install it in your home directory. - \begin{codesample4} - cd mercurial-\emph{version} - python setup.py install --force --home=\$HOME - \end{codesample4} -\end{enumerate} -Once the install finishes, Mercurial will be in the \texttt{bin} -subdirectory of your home directory. Don't forget to make sure that -this directory is present in your shell's search path. - -You will probably need to set the \envar{PYTHONPATH} environment -variable so that the Mercurial executable can find the rest of the -Mercurial packages. For example, on my laptop, I have set it to -\texttt{/home/bos/lib/python}. The exact path that you will need to -use depends on how Python was built for your system, but should be -easy to figure out. If you're uncertain, look through the output of -the installer script above, and see where the contents of the -\texttt{mercurial} directory were installed to. - -\section{On Windows} - -Building and installing Mercurial on Windows requires a variety of -tools, a fair amount of technical knowledge, and considerable -patience. I very much \emph{do not recommend} this route if you are a -``casual user''. Unless you intend to hack on Mercurial, I strongly -suggest that you use a binary package instead. - -If you are intent on building Mercurial from source on Windows, follow -the ``hard way'' directions on the Mercurial wiki at -\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall}, -and expect the process to involve a lot of fiddly work. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/template.tex --- a/fr/template.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,475 +0,0 @@ -\chapter{Customising the output of Mercurial} -\label{chap:template} - -Mercurial provides a powerful mechanism to let you control how it -displays information. The mechanism is based on templates. You can -use templates to generate specific output for a single command, or to -customise the entire appearance of the built-in web interface. - -\section{Using precanned output styles} -\label{sec:style} - -Packaged with Mercurial are some output styles that you can use -immediately. A style is simply a precanned template that someone -wrote and installed somewhere that Mercurial can find. - -Before we take a look at Mercurial's bundled styles, let's review its -normal output. - -\interaction{template.simple.normal} - -This is somewhat informative, but it takes up a lot of space---five -lines of output per changeset. The \texttt{compact} style reduces -this to three lines, presented in a sparse manner. - -\interaction{template.simple.compact} - -The \texttt{changelog} style hints at the expressive power of -Mercurial's templating engine. This style attempts to follow the GNU -Project's changelog guidelines\cite{web:changelog}. - -\interaction{template.simple.changelog} - -You will not be shocked to learn that Mercurial's default output style -is named \texttt{default}. - -\subsection{Setting a default style} - -You can modify the output style that Mercurial will use for every -command by editing your \hgrc\ file, naming the style you would -prefer to use. - -\begin{codesample2} - [ui] - style = compact -\end{codesample2} - -If you write a style of your own, you can use it by either providing -the path to your style file, or copying your style file into a -location where Mercurial can find it (typically the \texttt{templates} -subdirectory of your Mercurial install directory). - -\section{Commands that support styles and templates} - -All of Mercurial's ``\texttt{log}-like'' commands let you use styles -and templates: \hgcmd{incoming}, \hgcmd{log}, \hgcmd{outgoing}, and -\hgcmd{tip}. - -As I write this manual, these are so far the only commands that -support styles and templates. Since these are the most important -commands that need customisable output, there has been little pressure -from the Mercurial user community to add style and template support to -other commands. - -\section{The basics of templating} - -At its simplest, a Mercurial template is a piece of text. Some of the -text never changes, while other parts are \emph{expanded}, or replaced -with new text, when necessary. - -Before we continue, let's look again at a simple example of -Mercurial's normal output. - -\interaction{template.simple.normal} - -Now, let's run the same command, but using a template to change its -output. - -\interaction{template.simple.simplest} - -The example above illustrates the simplest possible template; it's -just a piece of static text, printed once for each changeset. The -\hgopt{log}{--template} option to the \hgcmd{log} command tells -Mercurial to use the given text as the template when printing each -changeset. - -Notice that the template string above ends with the text -``\Verb+\n+''. This is an \emph{escape sequence}, telling Mercurial -to print a newline at the end of each template item. If you omit this -newline, Mercurial will run each piece of output together. See -section~\ref{sec:template:escape} for more details of escape sequences. - -A template that prints a fixed string of text all the time isn't very -useful; let's try something a bit more complex. - -\interaction{template.simple.simplesub} - -As you can see, the string ``\Verb+{desc}+'' in the template has been -replaced in the output with the description of each changeset. Every -time Mercurial finds text enclosed in curly braces (``\texttt{\{}'' -and ``\texttt{\}}''), it will try to replace the braces and text with -the expansion of whatever is inside. To print a literal curly brace, -you must escape it, as described in section~\ref{sec:template:escape}. - -\section{Common template keywords} -\label{sec:template:keyword} - -You can start writing simple templates immediately using the keywords -below. - -\begin{itemize} -\item[\tplkword{author}] String. The unmodified author of the changeset. -\item[\tplkword{branches}] String. The name of the branch on which - the changeset was committed. Will be empty if the branch name was - \texttt{default}. -\item[\tplkword{date}] Date information. The date when the changeset - was committed. This is \emph{not} human-readable; you must pass it - through a filter that will render it appropriately. See - section~\ref{sec:template:filter} for more information on filters. - The date is expressed as a pair of numbers. The first number is a - Unix UTC timestamp (seconds since January 1, 1970); the second is - the offset of the committer's timezone from UTC, in seconds. -\item[\tplkword{desc}] String. The text of the changeset description. -\item[\tplkword{files}] List of strings. All files modified, added, or - removed by this changeset. -\item[\tplkword{file\_adds}] List of strings. Files added by this - changeset. -\item[\tplkword{file\_dels}] List of strings. Files removed by this - changeset. -\item[\tplkword{node}] String. The changeset identification hash, as a - 40-character hexadecimal string. -\item[\tplkword{parents}] List of strings. The parents of the - changeset. -\item[\tplkword{rev}] Integer. The repository-local changeset revision - number. -\item[\tplkword{tags}] List of strings. Any tags associated with the - changeset. -\end{itemize} - -A few simple experiments will show us what to expect when we use these -keywords; you can see the results in -figure~\ref{fig:template:keywords}. - -\begin{figure} - \interaction{template.simple.keywords} - \caption{Template keywords in use} - \label{fig:template:keywords} -\end{figure} - -As we noted above, the date keyword does not produce human-readable -output, so we must treat it specially. This involves using a -\emph{filter}, about which more in section~\ref{sec:template:filter}. - -\interaction{template.simple.datekeyword} - -\section{Escape sequences} -\label{sec:template:escape} - -Mercurial's templating engine recognises the most commonly used escape -sequences in strings. When it sees a backslash (``\Verb+\+'') -character, it looks at the following character and substitutes the two -characters with a single replacement, as described below. - -\begin{itemize} -\item[\Verb+\textbackslash\textbackslash+] Backslash, ``\Verb+\+'', - ASCII~134. -\item[\Verb+\textbackslash n+] Newline, ASCII~12. -\item[\Verb+\textbackslash r+] Carriage return, ASCII~15. -\item[\Verb+\textbackslash t+] Tab, ASCII~11. -\item[\Verb+\textbackslash v+] Vertical tab, ASCII~13. -\item[\Verb+\textbackslash \{+] Open curly brace, ``\Verb+{+'', ASCII~173. -\item[\Verb+\textbackslash \}+] Close curly brace, ``\Verb+}+'', ASCII~175. -\end{itemize} - -As indicated above, if you want the expansion of a template to contain -a literal ``\Verb+\+'', ``\Verb+{+'', or ``\Verb+{+'' character, you -must escape it. - -\section{Filtering keywords to change their results} -\label{sec:template:filter} - -Some of the results of template expansion are not immediately easy to -use. Mercurial lets you specify an optional chain of \emph{filters} -to modify the result of expanding a keyword. You have already seen a -common filter, \tplkwfilt{date}{isodate}, in action above, to make a -date readable. - -Below is a list of the most commonly used filters that Mercurial -supports. While some filters can be applied to any text, others can -only be used in specific circumstances. The name of each filter is -followed first by an indication of where it can be used, then a -description of its effect. - -\begin{itemize} -\item[\tplfilter{addbreaks}] Any text. Add an XHTML ``\Verb+
    +'' - tag before the end of every line except the last. For example, - ``\Verb+foo\nbar+'' becomes ``\Verb+foo
    \nbar+''. -\item[\tplkwfilt{date}{age}] \tplkword{date} keyword. Render the - age of the date, relative to the current time. Yields a string like - ``\Verb+10 minutes+''. -\item[\tplfilter{basename}] Any text, but most useful for the - \tplkword{files} keyword and its relatives. Treat the text as a - path, and return the basename. For example, ``\Verb+foo/bar/baz+'' - becomes ``\Verb+baz+''. -\item[\tplkwfilt{date}{date}] \tplkword{date} keyword. Render a date - in a similar format to the Unix \tplkword{date} command, but with - timezone included. Yields a string like - ``\Verb+Mon Sep 04 15:13:13 2006 -0700+''. -\item[\tplkwfilt{author}{domain}] Any text, but most useful for the - \tplkword{author} keyword. Finds the first string that looks like - an email address, and extract just the domain component. For - example, ``\Verb+Bryan O'Sullivan +'' becomes - ``\Verb+serpentine.com+''. -\item[\tplkwfilt{author}{email}] Any text, but most useful for the - \tplkword{author} keyword. Extract the first string that looks like - an email address. For example, - ``\Verb+Bryan O'Sullivan +'' becomes - ``\Verb+bos@serpentine.com+''. -\item[\tplfilter{escape}] Any text. Replace the special XML/XHTML - characters ``\Verb+&+'', ``\Verb+<+'' and ``\Verb+>+'' with - XML entities. -\item[\tplfilter{fill68}] Any text. Wrap the text to fit in 68 - columns. This is useful before you pass text through the - \tplfilter{tabindent} filter, and still want it to fit in an - 80-column fixed-font window. -\item[\tplfilter{fill76}] Any text. Wrap the text to fit in 76 - columns. -\item[\tplfilter{firstline}] Any text. Yield the first line of text, - without any trailing newlines. -\item[\tplkwfilt{date}{hgdate}] \tplkword{date} keyword. Render the - date as a pair of readable numbers. Yields a string like - ``\Verb+1157407993 25200+''. -\item[\tplkwfilt{date}{isodate}] \tplkword{date} keyword. Render the - date as a text string in ISO~8601 format. Yields a string like - ``\Verb+2006-09-04 15:13:13 -0700+''. -\item[\tplfilter{obfuscate}] Any text, but most useful for the - \tplkword{author} keyword. Yield the input text rendered as a - sequence of XML entities. This helps to defeat some particularly - stupid screen-scraping email harvesting spambots. -\item[\tplkwfilt{author}{person}] Any text, but most useful for the - \tplkword{author} keyword. Yield the text before an email address. - For example, ``\Verb+Bryan O'Sullivan +'' - becomes ``\Verb+Bryan O'Sullivan+''. -\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} keyword. Render a - date using the same format used in email headers. Yields a string - like ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+''. -\item[\tplkwfilt{node}{short}] Changeset hash. Yield the short form - of a changeset hash, i.e.~a 12-character hexadecimal string. -\item[\tplkwfilt{date}{shortdate}] \tplkword{date} keyword. Render - the year, month, and day of the date. Yields a string like - ``\Verb+2006-09-04+''. -\item[\tplfilter{strip}] Any text. Strip all leading and trailing - whitespace from the string. -\item[\tplfilter{tabindent}] Any text. Yield the text, with every line - except the first starting with a tab character. -\item[\tplfilter{urlescape}] Any text. Escape all characters that are - considered ``special'' by URL parsers. For example, \Verb+foo bar+ - becomes \Verb+foo%20bar+. -\item[\tplkwfilt{author}{user}] Any text, but most useful for the - \tplkword{author} keyword. Return the ``user'' portion of an email - address. For example, - ``\Verb+Bryan O'Sullivan +'' becomes - ``\Verb+bos+''. -\end{itemize} - -\begin{figure} - \interaction{template.simple.manyfilters} - \caption{Template filters in action} - \label{fig:template:filters} -\end{figure} - -\begin{note} - If you try to apply a filter to a piece of data that it cannot - process, Mercurial will fail and print a Python exception. For - example, trying to run the output of the \tplkword{desc} keyword - into the \tplkwfilt{date}{isodate} filter is not a good idea. -\end{note} - -\subsection{Combining filters} - -It is easy to combine filters to yield output in the form you would -like. The following chain of filters tidies up a description, then -makes sure that it fits cleanly into 68 columns, then indents it by a -further 8~characters (at least on Unix-like systems, where a tab is -conventionally 8~characters wide). - -\interaction{template.simple.combine} - -Note the use of ``\Verb+\t+'' (a tab character) in the template to -force the first line to be indented; this is necessary since -\tplkword{tabindent} indents all lines \emph{except} the first. - -Keep in mind that the order of filters in a chain is significant. The -first filter is applied to the result of the keyword; the second to -the result of the first filter; and so on. For example, using -\Verb+fill68|tabindent+ gives very different results from -\Verb+tabindent|fill68+. - - -\section{From templates to styles} - -A command line template provides a quick and simple way to format some -output. Templates can become verbose, though, and it's useful to be -able to give a template a name. A style file is a template with a -name, stored in a file. - -More than that, using a style file unlocks the power of Mercurial's -templating engine in ways that are not possible using the command line -\hgopt{log}{--template} option. - -\subsection{The simplest of style files} - -Our simple style file contains just one line: - -\interaction{template.simple.rev} - -This tells Mercurial, ``if you're printing a changeset, use the text -on the right as the template''. - -\subsection{Style file syntax} - -The syntax rules for a style file are simple. - -\begin{itemize} -\item The file is processed one line at a time. - -\item Leading and trailing white space are ignored. - -\item Empty lines are skipped. - -\item If a line starts with either of the characters ``\texttt{\#}'' or - ``\texttt{;}'', the entire line is treated as a comment, and skipped - as if empty. - -\item A line starts with a keyword. This must start with an - alphabetic character or underscore, and can subsequently contain any - alphanumeric character or underscore. (In regexp notation, a - keyword must match \Verb+[A-Za-z_][A-Za-z0-9_]*+.) - -\item The next element must be an ``\texttt{=}'' character, which can - be preceded or followed by an arbitrary amount of white space. - -\item If the rest of the line starts and ends with matching quote - characters (either single or double quote), it is treated as a - template body. - -\item If the rest of the line \emph{does not} start with a quote - character, it is treated as the name of a file; the contents of this - file will be read and used as a template body. -\end{itemize} - -\section{Style files by example} - -To illustrate how to write a style file, we will construct a few by -example. Rather than provide a complete style file and walk through -it, we'll mirror the usual process of developing a style file by -starting with something very simple, and walking through a series of -successively more complete examples. - -\subsection{Identifying mistakes in style files} - -If Mercurial encounters a problem in a style file you are working on, -it prints a terse error message that, once you figure out what it -means, is actually quite useful. - -\interaction{template.svnstyle.syntax.input} - -Notice that \filename{broken.style} attempts to define a -\texttt{changeset} keyword, but forgets to give any content for it. -When instructed to use this style file, Mercurial promptly complains. - -\interaction{template.svnstyle.syntax.error} - -This error message looks intimidating, but it is not too hard to -follow. - -\begin{itemize} -\item The first component is simply Mercurial's way of saying ``I am - giving up''. - \begin{codesample4} - \textbf{abort:} broken.style:1: parse error - \end{codesample4} - -\item Next comes the name of the style file that contains the error. - \begin{codesample4} - abort: \textbf{broken.style}:1: parse error - \end{codesample4} - -\item Following the file name is the line number where the error was - encountered. - \begin{codesample4} - abort: broken.style:\textbf{1}: parse error - \end{codesample4} - -\item Finally, a description of what went wrong. - \begin{codesample4} - abort: broken.style:1: \textbf{parse error} - \end{codesample4} - The description of the problem is not always clear (as in this - case), but even when it is cryptic, it is almost always trivial to - visually inspect the offending line in the style file and see what - is wrong. -\end{itemize} - -\subsection{Uniquely identifying a repository} - -If you would like to be able to identify a Mercurial repository -``fairly uniquely'' using a short string as an identifier, you can -use the first revision in the repository. -\interaction{template.svnstyle.id} -This is not guaranteed to be unique, but it is nevertheless useful in -many cases. -\begin{itemize} -\item It will not work in a completely empty repository, because such - a repository does not have a revision~zero. -\item Neither will it work in the (extremely rare) case where a - repository is a merge of two or more formerly independent - repositories, and you still have those repositories around. -\end{itemize} -Here are some uses to which you could put this identifier: -\begin{itemize} -\item As a key into a table for a database that manages repositories - on a server. -\item As half of a \{\emph{repository~ID}, \emph{revision~ID}\} tuple. - Save this information away when you run an automated build or other - activity, so that you can ``replay'' the build later if necessary. -\end{itemize} - -\subsection{Mimicking Subversion's output} - -Let's try to emulate the default output format used by another -revision control tool, Subversion. -\interaction{template.svnstyle.short} - -Since Subversion's output style is fairly simple, it is easy to -copy-and-paste a hunk of its output into a file, and replace the text -produced above by Subversion with the template values we'd like to see -expanded. -\interaction{template.svnstyle.template} - -There are a few small ways in which this template deviates from the -output produced by Subversion. -\begin{itemize} -\item Subversion prints a ``readable'' date (the ``\texttt{Wed, 27 Sep - 2006}'' in the example output above) in parentheses. Mercurial's - templating engine does not provide a way to display a date in this - format without also printing the time and time zone. -\item We emulate Subversion's printing of ``separator'' lines full of - ``\texttt{-}'' characters by ending the template with such a line. - We use the templating engine's \tplkword{header} keyword to print a - separator line as the first line of output (see below), thus - achieving similar output to Subversion. -\item Subversion's output includes a count in the header of the number - of lines in the commit message. We cannot replicate this in - Mercurial; the templating engine does not currently provide a filter - that counts the number of lines the template generates. -\end{itemize} -It took me no more than a minute or two of work to replace literal -text from an example of Subversion's output with some keywords and -filters to give the template above. The style file simply refers to -the template. -\interaction{template.svnstyle.style} - -We could have included the text of the template file directly in the -style file by enclosing it in quotes and replacing the newlines with -``\verb!\n!'' sequences, but it would have made the style file too -difficult to read. Readability is a good guide when you're trying to -decide whether some text belongs in a style file, or in a template -file that the style file points to. If the style file will look too -big or cluttered if you insert a literal piece of text, drop it into a -template instead. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-basic.tex --- a/fr/tour-basic.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,696 +0,0 @@ -\chapter{Un rapide tour de Mercurial} -\label{chap:tour-basic} - -\section{Installer Mercurial sur votre système} -\label{sec:tour:install} - -Des paquetages binaires de Mercurial sont disponibles pour la plupart -des systèmes d'exploitation, ce qui rend facile l'utilisation immédiate -de Mercurial sur votre ordinateur. - -\subsection{Linux} - -Parce que chaque distribution de Linux a ses propres outils de gestion -de paquets, politiques et rythmes de développements, il est difficile de -donner un ensemble d'instructions uniques pour installer les binaires de -Mercurial. La version de Mercurial avec laquelle vous vous retrouverez -dépendra grandement de l'activité de la personne en charge du paquetage -pour la distribution. - -Pour rester simple, je me concentrerai sur l'installation de Mercurial -en ligne de commande, sous les distributions les plus courantes. La -plupart des distributions fournissent des gestionnaires graphiques de -paquetage qui vous permettront d'installer Mercurial en quelques clicks. -Le paquetage devrait se nommer \textit{mercurial}. - -\begin{itemize} -\item[Debian] - \begin{codesample4} - apt-get install mercurial - \end{codesample4} - -\item[Fedora Core] - \begin{codesample4} - yum install mercurial - \end{codesample4} - -\item[Gentoo] - \begin{codesample4} - emerge mercurial - \end{codesample4} - -\item[OpenSUSE] - \begin{codesample4} - yum install mercurial - \end{codesample4} - -\item[Ubuntu] Le paquetage de Mercurial d'Ubuntu est construit sur celui de Debian. - Pour l'installer, exécutez simplement les commandes suivantes: - \begin{codesample4} - apt-get install mercurial - \end{codesample4} - Les paquetages Ubuntu pour Mercurial ont tendance à être un peu en retard - par rapport au paquetage Debian (au moment de l'écriture de ce livre, il - faut compter à peu près un retard de 7 mois), ce qui signifie que parfois - sur Ubuntu, vous risquez de rencontrer des problèmes qui ont été corrigés - depuis longtemps dans les paquetages Debian. -\end{itemize} - -\subsection{Solaris} - -SunFreeWare, à \url{http://www.saufreeware.com}, est une bonne source -pour trouver un vaste nombre de paquets précompilés pour 32 ou 64 bits -Intel et les architecture Sparc, dont les versions courantes de Mercurial. - -\subsection{Mac OS X} - -Lee Cantey publie un installateur de Mercurial pour Mac OS~X sur le site -\url{http://mercurial.berkwood.com}. Ce paquetage fonctionne sur les -architectures Intel-~et PowerPC. Avant de vous en servir, vous devez -installer une version Universelle MacPython~\cite{web:macpython}. C'est -assez facile à faire : suivez simplement les instructions sur le site -de Lee. - -Il est aussi possible d'installer Mercurial en utilisant Fink ou MacPorts, -deux outils de gestion de paquetage libres pour Mac OS X. Si vous avez -Fink, utilisez \command{sudo fink install mercurial-py25}. Si vous avez -MacPorts, \command{sudo port install mercurial}. - -\subsection{Windows} - -Lee Cantey publie aussi un installateur de Mercurial pour Windows sur le site -\url{http://mercurial.berkwood.com}. Ce paquetage n'a aucune dépendance -externe, il fonctionne ``tout court''. - -\begin{note} - La version de Windows de Mercurial ne convertie pas automatiquement - les retours chariot Windows et Unix. Si vous désirez partager votre - travail avec des utilisateurs Unix, vous devez faire un peu de configuration - supplémentaire. XXX En dire plus. -\end{note} - -\section{Commencer à utiliser Mercurial} - -Pour commencer, nous utiliserons la commande \hgcmd{version} pour vérifier -si Mercurial est installé proprement. Les informations affichées sur la -version ne sont pas réellement importantes en soit, c'est surtout de savoir -si elles s'affichent qui nous intéresse. -\interaction{tour.version} - -\subsection{L'aide intégrée} - -Mercurial fournit un système d'aide intégré, ce qui est inestimable quand -vous vous retrouvez coincé à essayer de vous rappeler comment lancer telle -ou telle commande. -Si c'est le cas, exécutez simplement \hgcmd{help}; il vous aidera à imprimer -une brève liste de commandes, avec une description de ce qu'elles font. Si vous -demandez de l'aide sur une commande spécifique (voir ci-dessous), il affichera -des informations plus détaillées. -\interaction{tour.help} -Pour un niveau d'informations encore plus détaillées (ce dont vous aurez rarement -besoin), exécuter \hgcmdargs{help}{\hggopt{-v}}. L'option \hggopt{-v} est -l'abréviation de \hggopt{--verbose}, et indique à Mercurial d'afficher plus -d'informations que d'habitude. - -\section{Travailler avec un dépôt} - -Avec Mercurial, tout se déroule au sein du \emph{dépôt}\footnote{NdT: Dépôt est -la traduction que j'ai retenue pour tout l'ouvrage du terme anglais \textit{repository}}. -Le dépôt d'un projet contient tous les fichiers qui ``appartiennent'' au projet. - -Il n'y a rien de particulièrement magique au sujet de ce dépôt, c'est -simplement une arborescence sur votre système de fichiers que Mercurial -traite de manière spéciale. Vous pouvez renommer ou effacer ce répertoire -à n'importe quel moment, en utilisant la ligne de commande ou votre -explorateur de fichiers. - -\subsection{Faire une copie locale de votre dépôt} - -\emph{Copier} un dépôt est juste un peu spécial. Bien que vous -puissiez utiliser une commande habituelle de copie pour copier -votre dépôt, il vaut mieux utiliser une commande fournie par -Mercurial. Cette commande est appelée \hgcmd{clone}, car elle -crée une copie identique à un dépôt existant. -\interaction{tour.clone} -Si votre opération de clonage réussit, vous devriez maintenant -avoir un répertoire local appelé \dirname{hello}. Ce répertoire -contiendra quelques fichiers. -\interaction{tour.ls} -Ces fichiers ont le même contenu et historique dans votre dépôt -qu'ils ont dans le dépôt que vous avez cloné. - -Chaque dépôt Mercurial est complet, autonome et indépendant. Il -contient sa propre copie privée des fichiers du projet et de leur -historique. Le clone d'un dépôt se souvient de la localisation du -dépôt à partir duquel il a été clôné, mais il ne communique pas avec -ce dernier, ou un autre, à moins que vous ne lui demandiez. - -Ce que tout ceci signifie pour le moment est que nous sommes libres -d'expérimenter avec ce dépôt, confiants dans le fait qu'il s'agit d'un -``bac à sable'' qui n'affectera personne d'autre. - -\subsection{Quel est le contenu d'un dépôt ?} - -Prêtons plus attention un instant au contenu d'un dépôt. Nous voyons -qu'il contient un répertoire nommé \dirname{.hg}. C'est ici que Mercurial -conserve toutes ses métadonnées. -\interaction{tour.ls-a} - -Le contenu du répertoire \dirname{.hg} et ses sous répertoires sont les -seuls propres à Mercurial. Tous les autres fichiers et répertoires dans -le dépôt sont à vous, et vous pouvez en faire ce que vous voulez. - -Pour introduire un peu de terminologie, le répertoire \dirname{.hg} est -un ``vrai'' dépôt, et tous les fichiers et les répertoires qui coexistent -avec lui, sont désignés sous le nom \emph{espace de travail}\footnote{NdT: -\textit{working directory}}. Une manière facile de se rappeler cette -distinction est de retenir que le \emph{dépôt} contient l'\emph{historique} -de votre projet, alors que l'\emph{espace de travail} contient une \emph{copie -ponctuelle}\footnote{NdT: Ce terme est une traduction du terme anglais -\textit{snapshot}. Il est traduit ici pour faciliter la lecture, mais ne sera -plus traduit par la suite.} de votre projet à un certain point de son -historique. - -\section{Une ballade dans l'historique} - -Une des premières choses que vous aurez envie de faire avec un nouveau -dépôt, sera de comprendre son historique. La commande \hgcmd{log} vous -donne une vue de l'historique. -\interaction{tour.log} -Par défaut, cette commande affiche à l'écran un bref paragraphe pour chaque -révision enregistrée pour ce projet. Dans la terminologie de Mercurial, nous -appelons chacun de ces évènements enregistrés un \emph{changeset}, parce -qu'il contient un ensemble de modifications sur plusieurs fichiers. - -La commande \hgcmd{log} affiche ainsi ces informations: -\begin{itemize} -\item[\texttt{changeset}] Ce champ contient un nombre, séparé par deux points - (:), d'une chaine hexadécimale. Il s'agit en fait d'\emph{identifiants} - d'un \textit{changeset}. Il y a deux identifiants car le numéro de - la révision est plus court et plus à facile à saisir qu'une séquence - hexadécimale. -\item[\texttt{user}] L'identité de la personne qui a créée ce %%% laisser le terme anglais car il sera affiché - \textit{changeset}. C'est un champ libre de forme, mais la plupart du - temps il contient le nom et l'email de la personne. -\item[\texttt{date}] La date et l'heure à laquelle le \textit{changeset} - a été créé, ainsi que le \textit{fuseau horaire} dans laquelle il a été créé. %%%TODO: Translate 'timezone' properly : FUSEAU - (La date et l'heure sont locales à ce \textit{fuseau}, elles indiquent - donc quelle date et heure il était pour la personne qui a créé ce %%%TODO: je suppose (quelle "heure") OUI - \textit{changeset}.) -\item[\texttt{résumé}] La première du message que le créateur a associé à - son \textit{changeset} pour le décrire. -\end{itemize} - -Par défaut, la commande \hgcmd{log} n'affiche qu'un résumé, il manque -beaucoup de détails. - -La figure~\ref{fig:tour-basic:history} fournit une représentation graphique -de l'historique du dépôt \dirname{hello}, pour rendre plus facile de voir -dans quelle direction l'historique se ``déroule''\footnote{NdT: \textit{flowing in}.}. -Nous reviendrons régulièrement sur cette représentation dans ce chapitre et -ceux qui suivent. - -\begin{figure}[ht] - \centering - \grafix{tour-history} - \caption{Représentation graphique du dépôt \dirname{hello} } - \label{fig:tour-basic:history} -\end{figure} - -\subsection{Changesets, révisions, et discuter avec les autres}%%%TODO: je propose : "discussion" (3 noms communs) -%%% je propose "colaboration" - -Comme l'anglais est réputé pour être un langage maladroit, et que l'informatique -est la source de bien des erreurs de terminologies (pourquoi utiliser un -seul terme quand quatre feront l'affaire ?), la gestion de version a une -variété de mots et de phrases qui veulent dire la même chose. Si vous -discutez d'historique de Mercurial avec d'autres personnes, -%%%TODO: ça ne veut rien dire: il faut supprimer une des personnes : soit "quelqu'un", -% soit "à d'autres personnes" -vous constaterez que souvent le mot ``\textit{changeset}'' est contracté simplement -en ``change'' ou (à l'écrit) ``cset'', et même parfois un -\textit{changeset} simplement ``révision'', abrégé en ``rev''. - -Bien que le \emph{mot} que vous utilisez pour désigner le concept de -\textit{changeset} importe peu, l'\emph{identifiant} que vous utilisez -pour désigner un \emph{changeset} \textit{spécifique} a une grande -importance. Rappelez vous que le champ \textit{changeset} affiché par la -commande \hgcmd{log} identifie un \textit{changeset} à la fois avec -un numéro de révision et une séquence hexadécimale. - -\begin{itemize} -\item Le numéro de révision est \emph{seulement valable dans ce dépôt}, -\item alors que la séquence hexadécimale est un \emph{identifiant - permanent, et invariant } qui pourra toujours être associé au - \textit{changeset} exact de \emph{chaque} copie de votre dépôt. -\end{itemize} - -La distinction est importante. Si vous envoyez un email à quelqu'un en -parlant de la ``révision 33'', il est très probable que sa révision~33 -\emph{ne sera pas la même} que la votre. La raison de ceci est que le -numéro de révision dépend de l'ordre dans lequel les modifications sont -arrivées dans le dépôt, et il n'y a aucune garantie que les mêmes changements -soient arrivés dans le même ordre dans différents dépôts. Trois modifications -$a,b,c$ peuvent aisément apparaitre dans un dépôt comme $0,1,2$, et dans -un autre comme $1,0,2$. - -Mercurial utilise les numéros de révision uniquement comme des raccourcis -pratiques. Si vous devez discuter d'un \textit{changeset} avec quelqu'un, -ou identifer un \textit{changeset} pour une quelquonque %%%TODO: our : "pour" ou "ou" -raison (par exemple, un rapport de \textit{bug}), utilisez la séquence -hexadécimale. - -\subsection{Afficher une révision spécifique} - -Pour réduire la sortie de \hgcmd{log} à une seule révision, utilisez -l'option \hgopt{log}{-r} (ou \hgopt{log}{--rev}). Vous pouvez utiliser -le numéro de révision ou la séquence hexadécimale comme identifiant, et -demander autant de révisions que vous le souhaitez. -\interaction{tour.log-r} - -Si vous voulez voir l'historique de plusieurs révisions sans avoir à -les énumérer, vous pouvez utiliser la \emph{\textit{range notation}} -\footnote{NdT: Il n'est pas aisé de traduire ce terme, donc je le %%%TODO : intervalle de numérotation ? -laisse en anglais} qui vous permet d'exprimer l'idée ``je veux toutes -les révisions entre $a$ et $b$, inclus''. -\interaction{tour.log.range} -Mercurial respecte aussi l'ordre dans lequel vous spécifiez les -révisions, ainsi \hgcmdargs{log}{-r 2:4} affichera $2,3,4$ alors que -\hgcmdargs{log}{-r 4:2} affichera $4,3,2$. - -\subsection{Informations détaillées} - - -Le résumé affiché par \hgcmd{log} est suffisant si vous savez déjà ce %%%TODO: je pense que le premier "si" est de trop : exact -que vous cherchez. En revanche, vous aurez probablement besoin de voir une description -complète du changement, ou une liste des fichiers modifiés si vous -cherchez à déterminer qu'un \textit{changeset} est bien celui que vous%%%TODO: les propositions sont mal construites : après un "si...." il faut une proposition sans "si... donc ici : "si ... recherchez", ben quoi ? -recherchez. L'option \hgopt{-v} de la commande \hgcmd{log} (ou -\hgopt{--verbose}) vous donne ces informations supplémentaires. -\interaction{tour.log-v} - -Si vous voulez voir à la fois la description et le contenu d'une -modification, ajouter l'option \hgopt{log}{-p} (ou \hgopt{log}{--patch}). -Ceci affiche le contenu d'une modification comme un \emph{diff unifié} -\footnote{NdT: \textit{unified diff}} (si vous n'avez jamais vu de diff -unifié avant, consultez la section~\ref{sec:mq:patch} pour un rapide -survol). - -\interaction{tour.log-vp} - -\section{Tout sur les options de commandes} - - -Avant d'aller plus loin sur le fonctionnement des commandes de Mercurial, -étudions un moment comment elles fonctionnent de manière générale. Vous -trouverez ça probablement utile pour la suite de notre parcours. - -Mercurial utilise une approche directe et cohérente pour interpréter %%%TODO: une manière d'approche ? -les options que vous passez aux commandes. Il suit une convention commune -à la plupart des systèmes Unix et Linux modernes. - -\begin{itemize} -\item Chaque option a un nom complet. Par exemple, comme nous l'avons déjà - vu, la commande \hgcmd{log} accepte l'option \hgopt{log}{--rev}.%%%TODO: commande ou command e\hgcmd...? -\item La plupart des options disposent de noms abrégés. Aussi, au lieu d'utiliser - \hgopt{log}{--rev}, vous pouvez utiliser \hgopt{log}{-r}. (Les options qui - n'ont pas de noms abrégés sont généralement rarement utilisées, pour cette raison). -\item Les noms complets commencent par deux tirets (i.e.~\hgopt{log}{--rev}), - alors que les options courtes commencent avec un seul (i.e.~\hgopt{log}{-r}). -\item Les noms des options sont cohérents entre les commandes. Par exemple, - chaque commande qui accepte un \textit{changeset~ID} ou un numéro de révision - accepte aussi \hgopt{log}{-r} et \hgopt{log}{--rev} comme arguments. - %TODO: Small mistake here, shouldn't have log here... shouldn't it ? -\end{itemize} - -Dans les exemples de ce livre, j'utilise les noms abrégés plutôt que les noms -complets. Ceci est une préférence personnelle, pas une recommandation. - -La plupart des commandes qui affichent une quelconque sortie à l'écran, -afficheront davantage avec l'option \hggopt{-v} (ou \hggopt{--verbose}), et -moins avec l'option \hggopt{-q} (ou \hggopt{--quiet}). - -\section{Faire et vérifier des modifications} - -Maintenant que nous avons une bonne idée des commandes pour consulter -l'historique de Mercurial, regardons comment faire des modifications et -les examiner. - - -La première chose que nous allons faire c'est isoler notre expérience dans -un dépôt à part. Nous allons utiliser la commande \hgcmd{clone}, mais nous -n'avons pas besoin de faire une copie de dépôt distant. Comme nous avons -déjà une copie locale, nous pouvons juste faire un clone de celle-ci à la -place. C'est beaucoup plus rapide que de faire une copie à travers le -réseau, et un dépôt cloné localement prend également moins d'espace disque. - -\interaction{tour.reclone} - -On notera au passage qu'il est souvent considéré comme une bonne pratique -de conserver une copie ``immaculée'' du dépôt distant, à partir de laquelle -vous pourrez faire des copies locales temporaires pour créer des ``bacs à -sable'' pour chaque tâche sur laquelle vous souhaitez travailler. Ceci vous -permet de travailler sur plusieurs choses en parallèle, chacune isolée les -unes des autres en attendant que ces tâches soient finies et que vous soyez -prêt à les réintégrer. Parce que les copies locales sont peu coûteuses, il -est très rapide de créer ou détruire des dépôts dès que vous en avez besoin. - -%% Note: la dernière phrase n'est pas une traduction littérale, mais je -%% pense qu'elle exprime plus clairement en français ce que veut dire son -%% équivalent anglais. : OUI - -Dans notre dépôt \dirname{my-hello}, nous avons un fichier \filename{hello.c} -qui contient le classique programme ``hello, world''. Nous allons utiliser -l'ancienne et vénérable commande \command{sed} pour l'éditer afin qu'il -affiche une seconde ligne à l'écran. (J'utilise \command{sed} seulement parce -qu'il est ainsi facile d'écrire des exemples sous forme de script. Comme -vous n'avez pas ces contraintes, vous n'utiliserez probablement pas \command{sed} -mais plutôt votre éditeur de texte favori). - -\interaction{tour.sed} - -La commande \hgcmd{status} de Mercurial nous dira de quels fichiers Mercurial -s'occupe au sein de ce dépôt. -\interaction{tour.status} -La commande \hgcmd{status} n'affiche rien sur la sortie pour quelques fichiers -mais une ligne commence par ``\texttt{M}'' for \filename{hello.c}. À moins que -vous ne lui indiquiez de le faire, \hgcmd{status} n'affichera aucune sortie -pour les fichiers qui n'ont pas été modifiés. - -Le caractère ``\texttt{M}'' indique que Mercurial a remarqué que nous avions -modifié le fichier \filename{hello.c}. Nous n'avons pas besoin d'\emph{informer} -Mercurial que nous allons modifier un fichier avant de le faire, ou que nous -venons de le modifier, il est capable de s'en rendre compte tout seul. - -C'est pratique de savoir que nous avons modifié \filename{hello.c}, mais il -serait encore plus pratique de savoir ce que nous avons modifié exactement. Pour -cela, nous avons la commande \hgcmd{diff}. - -\interaction{tour.diff} - -\section{Enregister les modifications dans un nouveau \textit{changeset}} - -Nous pouvons modifier des fichiers, compiler et tester nos modifications, -et utiliser les commandes \hgcmd{status} et \hgcmd{diff} pour voir les -modifications effectuées, jusqu'au moment où nous serons assez satisfaits -pour décider d'enregistrer notre travail dans un \textit{changeset}. - -La commande \hgcmd{commit} vous laisse créer un nouveau \textit{changeset}, -nous désignerons généralement cette opération par ``faire un commit'' ou -``commiter''\footnote{NdT: De mon expérience, la plupart des francophones -utilisent régulièrement, à l'oral, cette expression, mais bien évidement -il ne s'agit pas d'un terme de terminologie correcte, ni même français.} - -\subsection{Définir le nom d'utilisateur} - -Quand vous exécutez la commande \hgcmd{commit} pour la première fois, elle -n'est pas garantie de réussir. Mercurial enregistre votre nom et votre -adresse avec chaque modification que vous effectuez, de manière à ce que -vous soyez capable (ou d'autres le soient) de savoir qui a fait quelle -modification. Mercurial essaye automatiquement de découvrir un nom -d'utilisateur qui ait un minimum de sens pour effectuer l'opération -de \textit{commit} avec. Il va essayer chacune des méthodes suivantes, -dans l'ordre: -\begin{enumerate} -\item Si vous spécifiez l'option \hgopt{commit}{-u} avec la commande - \hgcmd{commit}, suivi d'un nom d'utilisateur, ceci aura toujours la - priorité sur les autres méthodes ci dessous. -\item Si vous avez défini une variable d'environnement \envar{HGUSER}, - c'est cette valeur qui est alors utilisée. -\item Si vous créez un fichier nommé \sfilename{.hgrc} dans votre - répertoire \textit{home}, avec une entrée \rcitem{ui}{username}, - c'est la valeur associée qui sera utilisée. Pour voir à quoi - ressemble le contenu de ce fichier regardez la - section~\ref{sec:tour-basic:username} ci-dessous. -\item Si vous avez défini une variable d'environnement \envar{EMAIL} - celle ci sera utilisée ensuite. -\item Enfin, Mercurial interrogera votre système pour trouver votre - nom d'utilisateur local ainsi que le nom de la machine hôte, et il - fabriquera un nom d'utilisateur à partir de ces données. Comme il arrive - souvent que ce genre de noms soit totalement inutile, il vous - préviendra en affichant un message d'avertissement. -\end{enumerate} - -Si tous ces mécanismes échouent, Mercurial n'exécutera pas la commande, -affichant un message d'erreur. Dans ce cas, il ne vous laissera pas -effectuer de \textit{commit} tant que vous n'aurez pas défini un nom -d'utilisateur. - -Vous devriez penser à utiliser la variable d'environement \envar{HGUSER} -et l'option \hgopt{commit}{-u} comme moyen pour \emph{changer le nom -d'utilisateur} par défaut. Pour une utilisation normale, la manière la plus -simple et robuste d'opérer est de créer un fichier \sfilename{.hgrc}, -voir ci-dessous pour les détails à ce sujet. - -\subsubsection{Créer un fichier de configuration pour Mercurial} -\label{sec:tour-basic:username} - -Pour définir un nom d'utilisateur, utilisez votre éditeur de texte favori -pour créer un fichier \sfilename{.hgrc} dans votre répertoire \textit{home}. -Mercurial va utiliser ce fichier pour retrouver votre configuration personnelle. -Le contenu initial devrait ressembler à ceci: -\begin{codesample2} - # This is a Mercurial configuration file. - [ui] - username = Firstname Lastname -\end{codesample2} -La ligne avec \texttt{[ui]} commence une \emph{section} du fichier de -configuration, ainsi la ligne ``\texttt{username = ...}'' signifie `` -définir la valeur de l'élément \texttt{username} dans la section -\texttt{ui}''. Une section continue jusqu'à ce qu'une nouvelle -commence, ou que la fin du fichier soit atteinte. Mercurial ignore -les lignes vides et traite tout texte situé à la suite d'un -``\texttt{\#}'' jusqu'à la fin de la ligne comme un commentaire. - -\subsubsection{Choisir un nom d'utilisateur} - -Vous pouvez utiliser n'importe quelle valeur pour votre \texttt{username}, -car cette information est destinée à d'autres personnes et non à être -interprétée par Mercurial. La convention que la plupart des personnes -<<<<<<< local -suivent est d'utiliser leurs noms suivies de leurs adresses emails, -comme montrée ci-dessus: - -\begin{note} - Le mécanisme interne du serveur \textit{web} intégré à Mercurial, - masque les adresses emails, pour rendre plus difficile leurs - récupérations par les outils utilisés par les \textit{spammmers}. - Ceci réduit la probabilité que de recevoir encore plus de - \textit{spam} si vous vous publiez un dépôt sur internet. -\end{note} - -\subsection{Rédiger un message de \textit{commit}} - -Lorsqu'on effectue une opération de \textit{commit}, Mercurial -lance automatiquement un éditeur de texte pour permettre de saisir -un message qui décrira les modifications effectuées dans ce -\textit{changeset}. Ce message est nommé le \emph{message de -\textit{commit}}. Ce sera un enregistrement pour tout lecteur -expliquant le pourquoi et le comment de vos modifications, et il sera -affiché par la commande \hgcmd{log}. -\interaction{tour.commit} - -L'éditeur que la commande \hgcmd{commit} déclenche ne contiendra -qu'une ligne vide suivi d'un certain nombre de lignes commençant -par ``\texttt{HG:}''. -\begin{codesample2} - \emph{empty line} - HG: changed hello.c -\end{codesample2} -Mercurial ignore les lignes qui commencent avec ``\texttt{HG:}'', il -ne les utilise que pour nous indiquer quels fichiers modifiés il se -prépare à \textit{commiter}. Modifier ou effacer ces lignes n'a -aucune conséquence sur l'opération de \textit{commit}. - -\subsection{Rédiger un message \textit{approprié}} - -Comme \hgcmd{log} n'affiche que la première ligne du message de -\textit{commit} par défaut, il est souvent considéré comme une bonne -pratique de rédiger des messages de \textit{commit} qui tiennent -sur une seule ligne. Voilà un exemple concret de message de -\textit{commit} qui \emph{ne suit pas} cette directive, et qui a donc -un résumé peu lisible. - -\begin{codesample2} - changeset: 73:584af0e231be - user: Censored Person - date: Tue Sep 26 21:37:07 2006 -0700 - summary: include buildmeister/commondefs. Add an exports and install -\end{codesample2} - -A ce sujet, il faut noter qu'il n'existe pas de règle absolue dans ce -domaine. Mercurial lui-même n'interprète pas les contenus des messages -de \textit{commit}, ainsi votre projet est libre de concevoir différentes -politiques de mise en page des messages. - -Ma préférence personnelle va au message court, mais informatif, qui offre -des précisions supplémentaires par rapport à ce que pourrait m'apprendre une commande -\hgcmdargs{log}{--patch}. - -\subsection{Annuler un \textit{commit}} - -Si, en rédigeant le message, vous décidez que finalement vous ne -voulez pas effectuer ce \textit{commit}, il suffit de quitter simplement -l'éditeur sans sauver. Ceci n'aura aucune conséquence sur le dépôt ou -les fichiers de l'espace de travail. - -Si vous exécuter la commande \hgcmd{commit} sans aucun argument, elle -enregistre toutes les modifications que vous avez faites, comme le montre -les commandes \hgcmd{status} et \hgcmd{diff}. - -\subsection{Admirer votre travail} - -Une fois que votre \textit{commit} est terminé, vous pouvez utiliser -la commande \hgcmd{tip} pour afficher le \textit{changeset} que nous -venons de créer. Cette commande produit une sortie à l'écran qui est -identique à celle du \hgcmd{log}, mais qui n'affiche que la dernière -révision du dépôt. -\interaction{tour.tip} -On fait couramment référence à la dernière révision du dépôt comme -étant la révision \textit{tip}, ou plus simplement le \textit{tip}. - -\section{Partager ses modifications} - -Nous avons mentionné plus haut que les dépôts de Mercurial -sont autosuffisants. Ce qui signifie que le \textit{changeset} -que vous venez de créer existe seulement dans votre répertoire -\dirname{my-hello}. Étudions comment propager cette modification -dans d'autres dépôts. - -\subsection{Récupérer les modifications d'autres dépôts} -\label{sec:tour:pull} - -Pour commencer, construisons un clone de notre dépôt \dirname{hello} -qui ne contiendra pas le changement que nous venons d'effectuer. Nous -l'appellerons notre dépôt temporaire \dirname{hello-pull}. - -\interaction{tour.clone-pull} - -Nous allons utiliser la commande \hgcmd{pull} pour apporter les -modifications depuis \dirname{my-hello} dans \dirname{hello-pull}. -Néanmoins, récupérer aveuglement des modifications depuis un dépôt -a quelque chose d'un peu effrayant. Mercurial propose donc une -commande \hgcmd{incoming} qui permet de savoir quelles modifications -la commande \hgcmd{pull} \emph{pourrait} entraîner dans notre dépôt, -et ceci sans effectuer réellement de modification dessus. -\interaction{tour.incoming} -(Bien évidement, quelqu'un pourrait ajouter des modifications -supplémentaires sur le dépôt que nous étudions avec \hgcmd{incoming}, -avant que nous ayons effectué notre \hgcmd{pull}, avec comme -triste conséquence que nous aurons récupéré des modifications que -nous n'attendions pas.) - -Apporter les modifications rapatriées dans un dépôt se résume donc -à exécuter la commande \hgcmd{pull}, et préciser depuis quel dépôt -effectuer le \hgcmd{pull}. -\interaction{tour.pull} - -Comme vous le voyez avec une sortie avant et après de la commande -\hgcmd{tip}, nous avons réussi à récupérer aisément les modifications -dans notre dépôt. Il reste néanmoins quelque chose à faire avant de -placer ces modifications dans l'espace de travail. - -\subsection{Mise à jour de l'espace de travail} - -Nous avons jusqu'à maintenant grossièrement définie la relation -entre un dépôt et un espace de travail. La commande \hgcmd{pull} que -nous avons exécutée dans la section~\ref{sec:tour:pull} a apporté -des modifications, que nous avons vérifiées, dans notre dépôt, mais -il n'y a aucune trace de ces modifications dans notre espace de travail. -En effet, \hgcmd{pull} ne touche pas (par défaut) à l'espace de -travail. C'est la commande \hgcmd{update} qui s'en charge. -\interaction{tour.update} - -Il peut sembler un peu étrange que la commande \hgcmd{pull} ne mette -pas à jour l'espace de travail automatiquement. Il y a en fait une -très bonne raison à cela : vous pouvez utilisez la commande - -\hgcmd{update} pour mettre à jour votre espace de travail à l'état -dans lequel il était à \emph{n'importe quelle révision} de l'historique -du dépôt. Si vous aviez un espace de travail contenant une ancienne -révision---pour chercher l'origine d'un \textit{bug}, par exemple---et -que vous effectuiez un \hgcmd{pull} qui mettrait à jour automatiquement -votre espace de travail, vous ne seriez probablement pas très satisfait. - -Néanmoins, comme les opérations de \textit{pull} sont très souvent -suivies d'un \textit{update}, Mercurial vous permet de combiner les -deux aisément en passant l'option \hgopt{pull}{-u} à la commande -\hgcmd{pull} -\begin{codesample2} - hg pull -u -\end{codesample2} - -Si vous étudiez de nouveau la sortie de la commande \hgcmd{pull} dans -la section~\ref{sec:tour:pull} quand nous l'avons exécutée sans l'option -\hgopt{pull}{-u}, vous pouvez constater qu'elle a affiché un rappel assez -utile : vous devez encore effectuer une opération pour mettre à jour -votre espace de travail: - -\begin{codesample2} - (run 'hg update' to get a working copy) -\end{codesample2} - -Pour découvrir sur quelle révision de l'espace de travail on est, utilisez -la commande \hgcmd{parents}. -\interaction{tour.parents} -Si vous regardez de nouveau le dessin~\ref{fig:tour-basic:history}, vous -<<<<<<< local -verrez les flèches reliant entre eux les \textit{changeset}. Le nœud -d'où la flèche \emph{part} est dans chaque cas un parent, -et le nœud où la flèche \emph{arrive} est un enfant. - -L'espace de travail a un parent de la même manière, c'est ce \textit{changeset} -que l'espace de travail contient à ce moment. -%%%TODO : difficile à comprendre : l'espace de travail a un parent, de la même manière, c'est ce changeset que l'espace... - -Pour mettre à jour l'espace de travail d'une révision particulière, -indiquez un numéro de révision ou un \textit{changeset~ID} à la commande -\hgcmd{update}. -\interaction{tour.older} -Si vous ne précisez pas de manière explicite de numéro de révision -la commande \hgcmd{update} mettra à jour votre espace de travail avec -le contenu de la révsion \textit{tip}, comme montré dans l'exemple -ci dessus lors du second appel à \hgcmd{update}. - -\subsection{Transférer les modifications à un autre dépôt} - -Mercurial vous laisse transférer les modifications à un autre -dépôt, depuis votre dépôt actuel. Comme dans l'exemple du -\hgcmd{pull} ci-dessus, nous allons créer un dépôt temporaire -vers lequel transférer\footnote{NdT: Les francophones disent souvent -``pousser'' tout simplement} nos modifications. -\interaction{tour.clone-push} -La commande \hgcmd{outgoing} nous indique quels changements nous -allons transférer vers l'autre serveur ? -\interaction{tour.outgoing} -Et la commande \hgcmd{push} effectue réellement le transfert. -\interaction{tour.push} -Comme avec \hgcmd{pull}, la commande \hgcmd{push} ne met pas à jour -le répertoire de travail du dépôt dans lequel il transfère les -modifications. (À l'inverse de \hgcmd{pull}, \hgcmd{push} ne fournit -pas d'option \texttt{-u} pour forcer la mise à jour de l'espace -de travail cible). - -Qu'est ce qui se passe lorsque vous essayez de récupérer ou de transférer -vos modifications et que le dépôt cible a déjà reçu ces modifications ? -Rien de bien excitant. -\interaction{tour.push.nothing} - -\subsection{Partager ses modifications à travers le réseau} - -Les commandes que nous avons étudiées dans les sections précédentes -ne sont pas limitées aux dépôt locaux. Chacune fonctionne de la même -manière à travers une connexion réseau, il suffit de lui passer une -URL à la place d'un chemin de fichier local. - -\interaction{tour.outgoing.net} -Dans cet exemple, nous allons voir quels changements nous pourrions -transférer vers le dépôt distant, mais le dépôt est, de manière tout -à fait compréhensible, pas configuré pour accepter des modifications -d'utilisateurs anonymes. -\interaction{tour.push.net} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-history.svg --- a/fr/tour-history.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - 0: REV0 - - 1: REV1 - - 2: REV2 - - 3: REV3 - - 4: REV4 - - - - - - (newest) - (oldest) - - 4: REV4 - - - revisionnumber - changesetidentifier - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-merge-conflict.svg --- a/fr/tour-merge-conflict.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - Greetings!I am Mariam Abacha, the wife of former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - - - - Greetings!I am Shehu Musa Abacha, cousin to the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - - - - Greetings!I am Alhaji Abba Abacha, son of the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - Base version - Our changes - Their changes - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-merge-merge.svg --- a/fr/tour-merge-merge.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,380 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - 4: REV4 - - - 5: REV_my_new_hello - - - 6: REV6_my_new_hello - - tip (and head) - head - - - - merge - working directoryduring merge - - 4: REV4 - - - 5: REV_my_new_hello - - - 6: REV6_my_new_hello - - tip - - - 7: REV7_my_new_hello - Working directory during merge - Repository after merge committed - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-merge-pull.svg --- a/fr/tour-merge-pull.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - 0: REV0 - - 1: REV1 - - 2: REV2 - - 3: REV3 - - 4: REV4 - - - - - - 5: REV_my_new_hello - - - 6: REV6_my_new_hello - - tip (and head) - head - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-merge-sep-repos.svg --- a/fr/tour-merge-sep-repos.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,466 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - 0: REV0 - - 1: REV1 - - 2: REV2 - - 3: REV3 - - 4: REV4 - - - - - - 5: REV_my_hello - - my-hello - - 0: REV0 - - 1: REV1 - - 2: REV2 - - 3: REV3 - - 4: REV4 - - - - - - 5: REV_my_new_hello - - my-new-hello - newest changesdiffer - common history - - - - - - - head revision(has no children) - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/tour-merge.tex --- a/fr/tour-merge.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,319 +0,0 @@ -\chapter{Un rapide tour de Mercurial: fusionner les travaux} -\label{chap:tour-merge} - -Nous avons maintenant étudié comment cloner un dépôt, effectuer -des changements dedans, et récupérer ou transférer depuis un -autre dépôt. La prochaine étape est donc de \emph{fusionner} les -modifications de différents dépôts. - -\section{Fusionner différents travaux} %%%TODO: better translation - %%% for 'Merging streams of work' ? -La fusion\footnote{NdT: Je garde fusion mais le jargon professionnel -emploiera généralement le terme \textit{merge}.} est un aspect -fondamental lorsqu'on travaille avec un gestionnaire de source -distribué. -\begin{itemize} -\item Alice et Bob ont chacun une copie personnelle du dépôt d'un - projet sur lequel ils collaborent. Alice corrige un \textit{bug} - dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le - sien. Ils veulent un dépôt partagé avec à la fois le correctif du - \textit{bug} et la nouvelle fonctionnalité. -\item Je travaille régulièrement sur plusieurs tâches différentes sur - un seul projet en même temps, chacun isolé dans son propre dépôt. - Travailler ainsi signifie que je dois régulièrement fusionner une - partie de mon code avec celui des autres. -\end{itemize} - -Parce que la fusion est une opération si commune à réaliser, -Mercurial la rend facile. Étudions ensemble le déroulement des opérations. -Nous commencerons encore par faire un clone d'un autre dépôt (vous voyez -que l'on fait ça tout le temps ?) puis nous ferons quelques modifications -dessus. -\interaction{tour.merge.clone} -Nous devrions avoir maintenant deux copies de \filename{hello.c} avec -des contenus différents. Les historiques de ces deux dépôts ont aussi -divergés, comme illustré dans la figure~\ref{fig:tour-merge:sep-repos}. - -\interaction{tour.merge.cat} - -\begin{figure}[ht] - \centering - \grafix{tour-merge-sep-repos} - \caption{Historiques récent divergents des dépôts \dirname{my-hello} - et \dirname{my-new-hello}} - \label{fig:tour-merge:sep-repos} -\end{figure} - -Nous savons déjà que récupérer les modifications depuis notre dépôt -\dirname{my-hello} n'aura aucun effet sur l'espace de travail. - -\interaction{tour.merge.pull} - -Néanmoins, la commande \hgcmd{pull} nous indique quelque chose au -sujet des ``heads''. - -\subsection{\textit{Head changesets}} %%%TODO: Hard (too?) to translate - -Une \textit{head}\footnote{NdT: Je garde \textit{head} que j'accorde -au féminin comme la coutume orale l'a imposé.} est un \textit{changeset} -sans descendants, ou enfants, comme on les désigne parfois. La révision -\textit{tip} est une \textit{head}, car la dernière révision dans un dépôt -n'a aucun enfant, mais il est important de noter qu'un dépôt peut contenir -plus d'une \textit{head}. - -\begin{figure}[ht] - \centering - \grafix{tour-merge-pull} - \caption{Contenu d'un dépôt après avoir transféré le contenu du dépôt - \dirname{my-hello} dans le dépôt \dirname{my-new-hello}} - \label{fig:tour-merge:pull} -\end{figure} - -Dans la figure~\ref{fig:tour-merge:pull}, vous pouvez constater l'effet -d'un \textit{pull} depuis le dépôt \dirname{my-hello} dans le dépôt -\dirname{my-new-hello}. L'historique qui était déjà présent dans le dépôt -\dirname{my-new-hello} reste intact, mais une nouvelle révision a été -ajoutée. En vous reportant à la figure~\ref{fig:tour-merge:sep-repos}, -vous pouvez voir que le \textit{\emph{changeset ID}} reste le même dans -le nouveau dépôt, mais que le \emph{numéro de révision} reste le même. -(Ceci est un parfait exemple de pourquoi il n'est fiable d'utiliser les -numéros de révision lorsque l'on discute d'un \textit{changeset}.) Vous -pouvez voir les \texit{heads} présentes dans le dépôt en utilisant la -commande \hgcmd{heads}. -\interaction{tour.merge.heads} - -\subsection{Effectuer la fusion} - -Que se passe-t-il quand vous essayez d'utiliser la commande \hgcmd{update} -pour mettre à jour votre espace de travail au nouveau \textit{tip}. -\interaction{tour.merge.update} -Mercurial nous prévient que la commande \hgcmd{update} n'effectuera pas -la fusion, il ne veut pas mettre à jour l'espace de travail quand il -estime que nous pourrions avoir besoin d'une fusion, à moins de lui -forcer la main. À la place, il faut utiliser la commande \hgcmd{merge} -pour fusionner les deux \textit{heads}. -\interaction{tour.merge.merge} - -\begin{figure}[ht] - \centering - \grafix{tour-merge-merge} - \caption{Espace de travail et dépôt lors d'une fusion, et dans le - \textit{commit} qui suit.} - \label{fig:tour-merge:merge} -\end{figure} - -Ceci met à jour l'espace de travail de manière à ce qu'il contienne -les modifications des \emph{deux} \textit{heads}, ce qui apparaît dans -les sorties de la commande \hgcmd{parents} et le contenu de -\filename{hello.c}. -\interaction{tour.merge.parents} - -\subsection{Effectuer le \textit{commit} du résultat de la fusion} - -Dès l'instant où vous avez effectué une fusion, \hgcmd{parents} vous -affichera deux parents, avant que vous n'exécutiez la commande -\hgcmd{commit} sur le résultat de la fusion. -\interaction{tour.merge.commit} -Nous avons maintenant un nouveau \textit{tip}, remarquer qu'il contient -\emph{à la fois} nos anciennes \textit{heads} et leurs parents. Ce sont -les mêmes révisions que nous avions affichées avec la commande -\hgcmd{parents}. - -\interaction{tour.merge.tip} -Dans la figure~\ref{fig:tour-merge:merge}, vous pouvez voir une représentation -de ce qui se passe dans l'espace de travail pendant la fusion, et comment ceci -affecte le dépôt lors du \textit{commit}. Pendant la fusion, l'espace de travail, -qui a deux \texit{changesets} comme parents, voit ces derniers devenir le parent -%%% TODO: le parent ou "les parents" : plus logique mais si il reste seulement -%%% un changeset, alors c'est effectivement un parent (le changeset est hermaphrodite) -d'un nouveau \textit{changeset}. - -\section{Fusionner les modifications en conflit} - -La plupart des fusions sont assez simple à réaliser, mais parfois -vous vous retrouverez à fusionner des fichiers où la modification touche -la même portion de code, au sein d'un même fichier. À moins que ces -modification ne soient identiques, ceci aboutira à un \emph{conflit}, -et vous devrez décider comment réconcilier les différentes modifications -dans un tout cohérent. - -\begin{figure}[ht] - \centering - \grafix{tour-merge-conflict} - \caption{Modifications conflictuelles dans un document} - \label{fig:tour-merge:conflict} -\end{figure} - -La figure~\ref{fig:tour-merge:conflict} illustre un cas de modifications -conflictuelles dans un document. Nous avons commencé avec une version simple -de ce fichier, puis nous avons ajouté des modifications, pendant que -quelqu'un d'autre modifiait le même texte. Notre tâche dans la résolution -du conflit est de décider à quoi le fichier devrait ressembler. - -Mercurial n'a pas de mécanisme interne pour gérer les conflits. -À la place, il exécute un programme externe appelé \command{hgmerge}. -Il s'agit d'un script shell qui est embarqué par Mercurial, vous -pouvez le modifier si vous le voulez. Ce qu'il fait par défaut est -d'essayer de trouver un des différents outils de fusion qui seront -probablement installés sur le système. Il commence par les outils -totalement automatiques, et si ils échouent (parce que la résolution -du conflit nécessite une intervention humaine) ou si ils sont absents, -le script tente d'exécuter certains outils graphiques de fusion. - -Il est aussi possible de demander à Mercurial d'exécuter un autre -programme ou un autre script au lieu de la commande \command{hgmerge}, -en définissant la variable d'environnement \envar{HGMERGE} avec le nom -du programme de votre choix. - -\subsection{Utiliser un outil graphique de fusion} - -Mon outil de fusion préféré est \command{kdiff3}, que j'utilise ici -pour illustrer les fonctionnalités classiques des outils graphiques -de fusion. Vous pouvez voir une capture d'écran de l'utilisation de -\command{kdiff3} dans la figure~\ref{fig:tour-merge:kdiff3}. Cet outil -effectue une \emph{fusion \textit{three-way}}, car il y a trois différentes -versions du fichier qui nous intéresse. Le fichier découpe la partie -supérieure de la fenêtre en trois panneaux: - -\begin{itemize} -\item A gauche on la version de \emph{base} du fichier, soit la plus - récente version des deux versions qu'on souhaite fusionner. -\item Au centre, il y a ``notre'' version du fichier, avec le contenu - que nous avons modifié. -\item Sur la droite, on trouve ``leur'' version du fichier, celui qui - contient le \textit{changeset} que nous souhaitons intégré. -\end{itemize} - -Dans le panneau en dessous, on trouve le \emph{résultat} actuel de notre -fusion. Notre tâche consiste donc à remplacement tous les textes en rouges, -qui indiquent des conflits non résolus, avec une fusion manuelle et pertinente -de ``notre'' version et de la ``leur''. - -Tous les quatre panneaux sont \emph{accrochés ensemble}, si nous déroulons -les ascenseurs verticalement ou horizontalement dans chacun d'entre eux, les -autres sont mis à jour avec la section correspondante dans leurs fichiers -respectifs. - -\begin{figure}[ht] - \centering - \grafix{kdiff3} - \caption{Utilisation de \command{kdiff3} pour fusionner différentes versions - d'un fichier.} - \label{fig:tour-merge:kdiff3} -\end{figure} - -Pour chaque portion de fichier posant problème, nous pouvons choisir -de résoudre le conflit en utilisant une combinaison -de texte depuis la version de base, la notre, ou la leur. Nous pouvons -aussi éditer manuellement les fichiers à tout moment, si c'est -nécessaire. - -Il y a \emph{beaucoup} d'outils de fusion disponibles, bien trop pour -en parler de tous ici. Leurs disponibilités varient selon les plate formes -ainsi que leurs avantages et inconvénients. La plupart sont optimisé pour -la fusion de fichier contenant un texte plat, certains sont spécialisé -dans un format de fichier précis (généralement XML). - -\subsection{Un exemple concret} - -Dans cet exemple, nous allons reproduire la modification de l'historique -du fichier de la figure~\ref{fig:tour-merge:conflict} ci dessus. Commençons -par créer un dépôt avec une version de base de notre document. - -\interaction{tour-merge-conflict.wife} -Créons un clone de ce dépôt et faisons une modification dans le fichier. -\interaction{tour-merge-conflict.cousin} -Et un autre clone, pour simuler que quelqu'un d'autre effectue une -modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare -de devoir effectuer des \textit{merge} avec vos propres travaux quand -vous isolez les tâches dans des dépôts distincts. En effet, vous -aurez alors à trouver et résoudre certains conflits). -\interaction{tour-merge-conflict.son} -Maintenant que ces deux versions différentes du même fichier sont -créées, nous allons configurer l'environnement de manière appropriée pour -exécuter notre \textit{merge}. -\interaction{tour-merge-conflict.pull} - -Dans cette exemple, je n'utiliserais pas la commande Mercurial -habituelle \command{hgmerge} pour effectuer le \textit{merge}, -car il me faudrait abandonner ce joli petit exemple automatisé -pour utiliser un outil graphique. À la place, je vais définir -la variable d'environnement \envar{HGMERGE} pour indiquer à -Mercurial d'utiliser la commande non-interactive \command{merge}. -Cette dernière est embarquée par de nombreux systèmes ``à la Unix''. -Si vous exécutez cet exemple depuis votre ordinateur, ne vous -occupez pas de définir \envar{HGMERGE}. -\interaction{tour-merge-conflict.merge} -Parce que \command{merge} ne peut pas résoudre les modifications -conflictuelles, il laisse des \emph{marqueurs de différences} -\footnote{NdT: Oui, je traduis \textit{merge markers} par un sens -inverse en Français, mais je pense vraiment que c'est plus clair -comme ça...} à l'intérieur du fichier qui a des conflits, indiquant -clairement quelles lignes sont en conflits, et si elles viennent de -notre fichier ou du fichier externe. - -Mercurial peut distinguer, à la manière dont la commande \command{merge} -se termine, qu'elle n'a pas été capable d'effectuer le \textit{merge}, -alors il nous indique que nous devons effectuer de nouveau cette -opération. Ceci peut être très utile si, par exemple, nous exécutons un -outil graphique de fusion et que nous le quittons sans nous rendre compte -qu'il reste des conflits ou simplement par erreur. - -Si le \textit{merge} automatique ou manuel échoue, il n'y a rien pour -nous empêcher de ``corriger le tir'' en modifiant nous même les fichiers, -et enfin effectuer le \textit{commit} du fichier: -\interaction{tour-merge-conflict.commit} - -\section{Simplification de la séquence pull-merge-commit} -\label{sec:tour-merge:fetch} - -La procédure pour effectuer la fusion indiquée ci-dessus est simple, -mais requiert le lancement de trois commandes à la suite. -\begin{codesample2} - hg pull - hg merge - hg commit -m 'Merged remote changes' -\end{codesample2} - -Lors du \textit{commit} final, vous devez également saisir un message, -qui aura vraisemblablement assez peu d'intérêt. - -Il serait assez sympathique de pouvoir réduire le nombre d'opérations -nécessaire, si possible. De fait Mercurial est fourni avec une -extension appelé \hgext{fetch} qui fait justement cela. - -Mercurial fourni un mécanisme d'extension flexible qui permet à chacun -d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial -léger et facile à utiliser. Certains extensions ajoutent de nouvelles -commandes que vous pouvez utiliser en ligne de commande, alors que -d'autres travaillent ``en coulisse,'' par exemple en ajoutant des -possibilités au serveur. - -L'extension \hgext{fetch} ajoute une nouvelle commande nommée, sans -surprise, \hgcmd{fetch}. Cette extension résulte en une combinaison -de \hgcmd{pull}, \hgcmd{update} and \hgcmd{merge}. Elle commence par -récupérer les modifications d'un autre dépôt dans le dépôt courant. -Si elle trouve que les modifications ajoutent une nouvelle \textit{head}, -elle effectue un \textit{merge}, et ensuite \texit{commit} le résultat -du \textit{merge} avec un message généré automatiquement. Si aucune -\textit{head} n'ont été ajouté, elle met à jour le répertoire de travail -au niveau du nouveau \textit{changeset} \textit{tip}. - - -Activer l'extension \hgext{fetch} est facile. Modifiez votre \sfilename{.hgrc}, -et soit allez à la section \rcsection{extensions} soit créer une -section \rcsection{extensions}. Ensuite ajoutez une ligne qui consiste -simplement en ``\Verb+fetch =''. - -\begin{codesample2} - [extensions] - fetch = -\end{codesample2} -(Normalement, sur la partie droite de ``\texttt{=}'' devrait apparaître -le chemin de l'extension, mais étant donné que l'extension \hgext{fetch} -fait partie de la distribution standard, Mercurial sait où la trouver.) - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/undo-manual-merge.dot --- a/fr/undo-manual-merge.dot Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -digraph undo_manual { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; - "third change" -> "manual\nmerge"; - backout -> "manual\nmerge"; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/undo-manual.dot --- a/fr/undo-manual.dot Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -digraph undo_manual { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/undo-non-tip.dot --- a/fr/undo-non-tip.dot Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -digraph undo_non_tip { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; - merge [label="automated\nmerge", shape=box]; - "third change" -> merge; - backout -> merge; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/undo-simple.dot --- a/fr/undo-simple.dot Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -digraph undo_simple { - "first change" -> "second change"; - "second change" -> "back out\nsecond change"; -} diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/undo.tex --- a/fr/undo.tex Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,767 +0,0 @@ -\chapter{Finding and fixing your mistakes} -\label{chap:undo} - -To err might be human, but to really handle the consequences well -takes a top-notch revision control system. In this chapter, we'll -discuss some of the techniques you can use when you find that a -problem has crept into your project. Mercurial has some highly -capable features that will help you to isolate the sources of -problems, and to handle them appropriately. - -\section{Erasing local history} - -\subsection{The accidental commit} - -I have the occasional but persistent problem of typing rather more -quickly than I can think, which sometimes results in me committing a -changeset that is either incomplete or plain wrong. In my case, the -usual kind of incomplete changeset is one in which I've created a new -source file, but forgotten to \hgcmd{add} it. A ``plain wrong'' -changeset is not as common, but no less annoying. - -\subsection{Rolling back a transaction} -\label{sec:undo:rollback} - -In section~\ref{sec:concepts:txn}, I mentioned that Mercurial treats -each modification of a repository as a \emph{transaction}. Every time -you commit a changeset or pull changes from another repository, -Mercurial remembers what you did. You can undo, or \emph{roll back}, -exactly one of these actions using the \hgcmd{rollback} command. (See -section~\ref{sec:undo:rollback-after-push} for an important caveat -about the use of this command.) - -Here's a mistake that I often find myself making: committing a change -in which I've created a new file, but forgotten to \hgcmd{add} it. -\interaction{rollback.commit} -Looking at the output of \hgcmd{status} after the commit immediately -confirms the error. -\interaction{rollback.status} -The commit captured the changes to the file \filename{a}, but not the -new file \filename{b}. If I were to push this changeset to a -repository that I shared with a colleague, the chances are high that -something in \filename{a} would refer to \filename{b}, which would not -be present in their repository when they pulled my changes. I would -thus become the object of some indignation. - -However, luck is with me---I've caught my error before I pushed the -changeset. I use the \hgcmd{rollback} command, and Mercurial makes -that last changeset vanish. -\interaction{rollback.rollback} -Notice that the changeset is no longer present in the repository's -history, and the working directory once again thinks that the file -\filename{a} is modified. The commit and rollback have left the -working directory exactly as it was prior to the commit; the changeset -has been completely erased. I can now safely \hgcmd{add} the file -\filename{b}, and rerun my commit. -\interaction{rollback.add} - -\subsection{The erroneous pull} - -It's common practice with Mercurial to maintain separate development -branches of a project in different repositories. Your development -team might have one shared repository for your project's ``0.9'' -release, and another, containing different changes, for the ``1.0'' -release. - -Given this, you can imagine that the consequences could be messy if -you had a local ``0.9'' repository, and accidentally pulled changes -from the shared ``1.0'' repository into it. At worst, you could be -paying insufficient attention, and push those changes into the shared -``0.9'' tree, confusing your entire team (but don't worry, we'll -return to this horror scenario later). However, it's more likely that -you'll notice immediately, because Mercurial will display the URL it's -pulling from, or you will see it pull a suspiciously large number of -changes into the repository. - -The \hgcmd{rollback} command will work nicely to expunge all of the -changesets that you just pulled. Mercurial groups all changes from -one \hgcmd{pull} into a single transaction, so one \hgcmd{rollback} is -all you need to undo this mistake. - -\subsection{Rolling back is useless once you've pushed} -\label{sec:undo:rollback-after-push} - -The value of the \hgcmd{rollback} command drops to zero once you've -pushed your changes to another repository. Rolling back a change -makes it disappear entirely, but \emph{only} in the repository in -which you perform the \hgcmd{rollback}. Because a rollback eliminates -history, there's no way for the disappearance of a change to propagate -between repositories. - -If you've pushed a change to another repository---particularly if it's -a shared repository---it has essentially ``escaped into the wild,'' -and you'll have to recover from your mistake in a different way. What -will happen if you push a changeset somewhere, then roll it back, then -pull from the repository you pushed to, is that the changeset will -reappear in your repository. - -(If you absolutely know for sure that the change you want to roll back -is the most recent change in the repository that you pushed to, -\emph{and} you know that nobody else could have pulled it from that -repository, you can roll back the changeset there, too, but you really -should really not rely on this working reliably. If you do this, -sooner or later a change really will make it into a repository that -you don't directly control (or have forgotten about), and come back to -bite you.) - -\subsection{You can only roll back once} - -Mercurial stores exactly one transaction in its transaction log; that -transaction is the most recent one that occurred in the repository. -This means that you can only roll back one transaction. If you expect -to be able to roll back one transaction, then its predecessor, this is -not the behaviour you will get. -\interaction{rollback.twice} -Once you've rolled back one transaction in a repository, you can't -roll back again in that repository until you perform another commit or -pull. - -\section{Reverting the mistaken change} - -If you make a modification to a file, and decide that you really -didn't want to change the file at all, and you haven't yet committed -your changes, the \hgcmd{revert} command is the one you'll need. It -looks at the changeset that's the parent of the working directory, and -restores the contents of the file to their state as of that changeset. -(That's a long-winded way of saying that, in the normal case, it -undoes your modifications.) - -Let's illustrate how the \hgcmd{revert} command works with yet another -small example. We'll begin by modifying a file that Mercurial is -already tracking. -\interaction{daily.revert.modify} -If we don't want that change, we can simply \hgcmd{revert} the file. -\interaction{daily.revert.unmodify} -The \hgcmd{revert} command provides us with an extra degree of safety -by saving our modified file with a \filename{.orig} extension. -\interaction{daily.revert.status} - -Here is a summary of the cases that the \hgcmd{revert} command can -deal with. We will describe each of these in more detail in the -section that follows. -\begin{itemize} -\item If you modify a file, it will restore the file to its unmodified - state. -\item If you \hgcmd{add} a file, it will undo the ``added'' state of - the file, but leave the file itself untouched. -\item If you delete a file without telling Mercurial, it will restore - the file to its unmodified contents. -\item If you use the \hgcmd{remove} command to remove a file, it will - undo the ``removed'' state of the file, and restore the file to its - unmodified contents. -\end{itemize} - -\subsection{File management errors} -\label{sec:undo:mgmt} - -The \hgcmd{revert} command is useful for more than just modified -files. It lets you reverse the results of all of Mercurial's file -management commands---\hgcmd{add}, \hgcmd{remove}, and so on. - -If you \hgcmd{add} a file, then decide that in fact you don't want -Mercurial to track it, use \hgcmd{revert} to undo the add. Don't -worry; Mercurial will not modify the file in any way. It will just -``unmark'' the file. -\interaction{daily.revert.add} - -Similarly, if you ask Mercurial to \hgcmd{remove} a file, you can use -\hgcmd{revert} to restore it to the contents it had as of the parent -of the working directory. -\interaction{daily.revert.remove} -This works just as well for a file that you deleted by hand, without -telling Mercurial (recall that in Mercurial terminology, this kind of -file is called ``missing''). -\interaction{daily.revert.missing} - -If you revert a \hgcmd{copy}, the copied-to file remains in your -working directory afterwards, untracked. Since a copy doesn't affect -the copied-from file in any way, Mercurial doesn't do anything with -the copied-from file. -\interaction{daily.revert.copy} - -\subsubsection{A slightly special case: reverting a rename} - -If you \hgcmd{rename} a file, there is one small detail that -you should remember. When you \hgcmd{revert} a rename, it's not -enough to provide the name of the renamed-to file, as you can see -here. -\interaction{daily.revert.rename} -As you can see from the output of \hgcmd{status}, the renamed-to file -is no longer identified as added, but the renamed-\emph{from} file is -still removed! This is counter-intuitive (at least to me), but at -least it's easy to deal with. -\interaction{daily.revert.rename-orig} -So remember, to revert a \hgcmd{rename}, you must provide \emph{both} -the source and destination names. - -% TODO: the output doesn't look like it will be removed! - -(By the way, if you rename a file, then modify the renamed-to file, -then revert both components of the rename, when Mercurial restores the -file that was removed as part of the rename, it will be unmodified. -If you need the modifications in the renamed-to file to show up in the -renamed-from file, don't forget to copy them over.) - -These fiddly aspects of reverting a rename arguably constitute a small -bug in Mercurial. - -\section{Dealing with committed changes} - -Consider a case where you have committed a change $a$, and another -change $b$ on top of it; you then realise that change $a$ was -incorrect. Mercurial lets you ``back out'' an entire changeset -automatically, and building blocks that let you reverse part of a -changeset by hand. - -Before you read this section, here's something to keep in mind: the -\hgcmd{backout} command undoes changes by \emph{adding} history, not -by modifying or erasing it. It's the right tool to use if you're -fixing bugs, but not if you're trying to undo some change that has -catastrophic consequences. To deal with those, see -section~\ref{sec:undo:aaaiiieee}. - -\subsection{Backing out a changeset} - -The \hgcmd{backout} command lets you ``undo'' the effects of an entire -changeset in an automated fashion. Because Mercurial's history is -immutable, this command \emph{does not} get rid of the changeset you -want to undo. Instead, it creates a new changeset that -\emph{reverses} the effect of the to-be-undone changeset. - -The operation of the \hgcmd{backout} command is a little intricate, so -let's illustrate it with some examples. First, we'll create a -repository with some simple changes. -\interaction{backout.init} - -The \hgcmd{backout} command takes a single changeset ID as its -argument; this is the changeset to back out. Normally, -\hgcmd{backout} will drop you into a text editor to write a commit -message, so you can record why you're backing the change out. In this -example, we provide a commit message on the command line using the -\hgopt{backout}{-m} option. - -\subsection{Backing out the tip changeset} - -We're going to start by backing out the last changeset we committed. -\interaction{backout.simple} -You can see that the second line from \filename{myfile} is no longer -present. Taking a look at the output of \hgcmd{log} gives us an idea -of what the \hgcmd{backout} command has done. -\interaction{backout.simple.log} -Notice that the new changeset that \hgcmd{backout} has created is a -child of the changeset we backed out. It's easier to see this in -figure~\ref{fig:undo:backout}, which presents a graphical view of the -change history. As you can see, the history is nice and linear. - -\begin{figure}[htb] - \centering - \grafix{undo-simple} - \caption{Backing out a change using the \hgcmd{backout} command} - \label{fig:undo:backout} -\end{figure} - -\subsection{Backing out a non-tip change} - -If you want to back out a change other than the last one you -committed, pass the \hgopt{backout}{--merge} option to the -\hgcmd{backout} command. -\interaction{backout.non-tip.clone} -This makes backing out any changeset a ``one-shot'' operation that's -usually simple and fast. -\interaction{backout.non-tip.backout} - -If you take a look at the contents of \filename{myfile} after the -backout finishes, you'll see that the first and third changes are -present, but not the second. -\interaction{backout.non-tip.cat} - -As the graphical history in figure~\ref{fig:undo:backout-non-tip} -illustrates, Mercurial actually commits \emph{two} changes in this -kind of situation (the box-shaped nodes are the ones that Mercurial -commits automatically). Before Mercurial begins the backout process, -it first remembers what the current parent of the working directory -is. It then backs out the target changeset, and commits that as a -changeset. Finally, it merges back to the previous parent of the -working directory, and commits the result of the merge. - -% TODO: to me it looks like mercurial doesn't commit the second merge automatically! - -\begin{figure}[htb] - \centering - \grafix{undo-non-tip} - \caption{Automated backout of a non-tip change using the \hgcmd{backout} command} - \label{fig:undo:backout-non-tip} -\end{figure} - -The result is that you end up ``back where you were'', only with some -extra history that undoes the effect of the changeset you wanted to -back out. - -\subsubsection{Always use the \hgopt{backout}{--merge} option} - -In fact, since the \hgopt{backout}{--merge} option will do the ``right -thing'' whether or not the changeset you're backing out is the tip -(i.e.~it won't try to merge if it's backing out the tip, since there's -no need), you should \emph{always} use this option when you run the -\hgcmd{backout} command. - -\subsection{Gaining more control of the backout process} - -While I've recommended that you always use the -\hgopt{backout}{--merge} option when backing out a change, the -\hgcmd{backout} command lets you decide how to merge a backout -changeset. Taking control of the backout process by hand is something -you will rarely need to do, but it can be useful to understand what -the \hgcmd{backout} command is doing for you automatically. To -illustrate this, let's clone our first repository, but omit the -backout change that it contains. - -\interaction{backout.manual.clone} -As with our earlier example, We'll commit a third changeset, then back -out its parent, and see what happens. -\interaction{backout.manual.backout} -Our new changeset is again a descendant of the changeset we backout -out; it's thus a new head, \emph{not} a descendant of the changeset -that was the tip. The \hgcmd{backout} command was quite explicit in -telling us this. -\interaction{backout.manual.log} - -Again, it's easier to see what has happened by looking at a graph of -the revision history, in figure~\ref{fig:undo:backout-manual}. This -makes it clear that when we use \hgcmd{backout} to back out a change -other than the tip, Mercurial adds a new head to the repository (the -change it committed is box-shaped). - -\begin{figure}[htb] - \centering - \grafix{undo-manual} - \caption{Backing out a change using the \hgcmd{backout} command} - \label{fig:undo:backout-manual} -\end{figure} - -After the \hgcmd{backout} command has completed, it leaves the new -``backout'' changeset as the parent of the working directory. -\interaction{backout.manual.parents} -Now we have two isolated sets of changes. -\interaction{backout.manual.heads} - -Let's think about what we expect to see as the contents of -\filename{myfile} now. The first change should be present, because -we've never backed it out. The second change should be missing, as -that's the change we backed out. Since the history graph shows the -third change as a separate head, we \emph{don't} expect to see the -third change present in \filename{myfile}. -\interaction{backout.manual.cat} -To get the third change back into the file, we just do a normal merge -of our two heads. -\interaction{backout.manual.merge} -Afterwards, the graphical history of our repository looks like -figure~\ref{fig:undo:backout-manual-merge}. - -\begin{figure}[htb] - \centering - \grafix{undo-manual-merge} - \caption{Manually merging a backout change} - \label{fig:undo:backout-manual-merge} -\end{figure} - -\subsection{Why \hgcmd{backout} works as it does} - -Here's a brief description of how the \hgcmd{backout} command works. -\begin{enumerate} -\item It ensures that the working directory is ``clean'', i.e.~that - the output of \hgcmd{status} would be empty. -\item It remembers the current parent of the working directory. Let's - call this changeset \texttt{orig} -\item It does the equivalent of a \hgcmd{update} to sync the working - directory to the changeset you want to back out. Let's call this - changeset \texttt{backout} -\item It finds the parent of that changeset. Let's call that - changeset \texttt{parent}. -\item For each file that the \texttt{backout} changeset affected, it - does the equivalent of a \hgcmdargs{revert}{-r parent} on that file, - to restore it to the contents it had before that changeset was - committed. -\item It commits the result as a new changeset. This changeset has - \texttt{backout} as its parent. -\item If you specify \hgopt{backout}{--merge} on the command line, it - merges with \texttt{orig}, and commits the result of the merge. -\end{enumerate} - -An alternative way to implement the \hgcmd{backout} command would be -to \hgcmd{export} the to-be-backed-out changeset as a diff, then use -the \cmdopt{patch}{--reverse} option to the \command{patch} command to -reverse the effect of the change without fiddling with the working -directory. This sounds much simpler, but it would not work nearly as -well. - -The reason that \hgcmd{backout} does an update, a commit, a merge, and -another commit is to give the merge machinery the best chance to do a -good job when dealing with all the changes \emph{between} the change -you're backing out and the current tip. - -If you're backing out a changeset that's~100 revisions back in your -project's history, the chances that the \command{patch} command will -be able to apply a reverse diff cleanly are not good, because -intervening changes are likely to have ``broken the context'' that -\command{patch} uses to determine whether it can apply a patch (if -this sounds like gibberish, see \ref{sec:mq:patch} for a -discussion of the \command{patch} command). Also, Mercurial's merge -machinery will handle files and directories being renamed, permission -changes, and modifications to binary files, none of which -\command{patch} can deal with. - -\section{Changes that should never have been} -\label{sec:undo:aaaiiieee} - -Most of the time, the \hgcmd{backout} command is exactly what you need -if you want to undo the effects of a change. It leaves a permanent -record of exactly what you did, both when committing the original -changeset and when you cleaned up after it. - -On rare occasions, though, you may find that you've committed a change -that really should not be present in the repository at all. For -example, it would be very unusual, and usually considered a mistake, -to commit a software project's object files as well as its source -files. Object files have almost no intrinsic value, and they're -\emph{big}, so they increase the size of the repository and the amount -of time it takes to clone or pull changes. - -Before I discuss the options that you have if you commit a ``brown -paper bag'' change (the kind that's so bad that you want to pull a -brown paper bag over your head), let me first discuss some approaches -that probably won't work. - -Since Mercurial treats history as accumulative---every change builds -on top of all changes that preceded it---you generally can't just make -disastrous changes disappear. The one exception is when you've just -committed a change, and it hasn't been pushed or pulled into another -repository. That's when you can safely use the \hgcmd{rollback} -command, as I detailed in section~\ref{sec:undo:rollback}. - -After you've pushed a bad change to another repository, you -\emph{could} still use \hgcmd{rollback} to make your local copy of the -change disappear, but it won't have the consequences you want. The -change will still be present in the remote repository, so it will -reappear in your local repository the next time you pull. - -If a situation like this arises, and you know which repositories your -bad change has propagated into, you can \emph{try} to get rid of the -changeefrom \emph{every} one of those repositories. This is, of -course, not a satisfactory solution: if you miss even a single -repository while you're expunging, the change is still ``in the -wild'', and could propagate further. - -If you've committed one or more changes \emph{after} the change that -you'd like to see disappear, your options are further reduced. -Mercurial doesn't provide a way to ``punch a hole'' in history, -leaving changesets intact. - -XXX This needs filling out. The \texttt{hg-replay} script in the -\texttt{examples} directory works, but doesn't handle merge -changesets. Kind of an important omission. - -\subsection{Protect yourself from ``escaped'' changes} - -If you've committed some changes to your local repository and they've -been pushed or pulled somewhere else, this isn't necessarily a -disaster. You can protect yourself ahead of time against some classes -of bad changeset. This is particularly easy if your team usually -pulls changes from a central repository. - -By configuring some hooks on that repository to validate incoming -changesets (see chapter~\ref{chap:hook}), you can automatically -prevent some kinds of bad changeset from being pushed to the central -repository at all. With such a configuration in place, some kinds of -bad changeset will naturally tend to ``die out'' because they can't -propagate into the central repository. Better yet, this happens -without any need for explicit intervention. - -For instance, an incoming change hook that verifies that a changeset -will actually compile can prevent people from inadvertantly ``breaking -the build''. - -\section{Finding the source of a bug} -\label{sec:undo:bisect} - -While it's all very well to be able to back out a changeset that -introduced a bug, this requires that you know which changeset to back -out. Mercurial provides an invaluable command, called -\hgcmd{bisect}, that helps you to automate this process and accomplish -it very efficiently. - -The idea behind the \hgcmd{bisect} command is that a changeset has -introduced some change of behaviour that you can identify with a -simple binary test. You don't know which piece of code introduced the -change, but you know how to test for the presence of the bug. The -\hgcmd{bisect} command uses your test to direct its search for the -changeset that introduced the code that caused the bug. - -Here are a few scenarios to help you understand how you might apply -this command. -\begin{itemize} -\item The most recent version of your software has a bug that you - remember wasn't present a few weeks ago, but you don't know when it - was introduced. Here, your binary test checks for the presence of - that bug. -\item You fixed a bug in a rush, and now it's time to close the entry - in your team's bug database. The bug database requires a changeset - ID when you close an entry, but you don't remember which changeset - you fixed the bug in. Once again, your binary test checks for the - presence of the bug. -\item Your software works correctly, but runs~15\% slower than the - last time you measured it. You want to know which changeset - introduced the performance regression. In this case, your binary - test measures the performance of your software, to see whether it's - ``fast'' or ``slow''. -\item The sizes of the components of your project that you ship - exploded recently, and you suspect that something changed in the way - you build your project. -\end{itemize} - -From these examples, it should be clear that the \hgcmd{bisect} -command is not useful only for finding the sources of bugs. You can -use it to find any ``emergent property'' of a repository (anything -that you can't find from a simple text search of the files in the -tree) for which you can write a binary test. - -We'll introduce a little bit of terminology here, just to make it -clear which parts of the search process are your responsibility, and -which are Mercurial's. A \emph{test} is something that \emph{you} run -when \hgcmd{bisect} chooses a changeset. A \emph{probe} is what -\hgcmd{bisect} runs to tell whether a revision is good. Finally, -we'll use the word ``bisect'', as both a noun and a verb, to stand in -for the phrase ``search using the \hgcmd{bisect} command. - -One simple way to automate the searching process would be simply to -probe every changeset. However, this scales poorly. If it took ten -minutes to test a single changeset, and you had 10,000 changesets in -your repository, the exhaustive approach would take on average~35 -\emph{days} to find the changeset that introduced a bug. Even if you -knew that the bug was introduced by one of the last 500 changesets, -and limited your search to those, you'd still be looking at over 40 -hours to find the changeset that introduced your bug. - -What the \hgcmd{bisect} command does is use its knowledge of the -``shape'' of your project's revision history to perform a search in -time proportional to the \emph{logarithm} of the number of changesets -to check (the kind of search it performs is called a dichotomic -search). With this approach, searching through 10,000 changesets will -take less than three hours, even at ten minutes per test (the search -will require about 14 tests). Limit your search to the last hundred -changesets, and it will take only about an hour (roughly seven tests). - -The \hgcmd{bisect} command is aware of the ``branchy'' nature of a -Mercurial project's revision history, so it has no problems dealing -with branches, merges, or multiple heads in a repository. It can -prune entire branches of history with a single probe, which is how it -operates so efficiently. - -\subsection{Using the \hgcmd{bisect} command} - -Here's an example of \hgcmd{bisect} in action. - -\begin{note} - In versions 0.9.5 and earlier of Mercurial, \hgcmd{bisect} was not a - core command: it was distributed with Mercurial as an extension. - This section describes the built-in command, not the old extension. -\end{note} - -Now let's create a repository, so that we can try out the -\hgcmd{bisect} command in isolation. -\interaction{bisect.init} -We'll simulate a project that has a bug in it in a simple-minded way: -create trivial changes in a loop, and nominate one specific change -that will have the ``bug''. This loop creates 35 changesets, each -adding a single file to the repository. We'll represent our ``bug'' -with a file that contains the text ``i have a gub''. -\interaction{bisect.commits} - -The next thing that we'd like to do is figure out how to use the -\hgcmd{bisect} command. We can use Mercurial's normal built-in help -mechanism for this. -\interaction{bisect.help} - -The \hgcmd{bisect} command works in steps. Each step proceeds as follows. -\begin{enumerate} -\item You run your binary test. - \begin{itemize} - \item If the test succeeded, you tell \hgcmd{bisect} by running the - \hgcmdargs{bisect}{good} command. - \item If it failed, run the \hgcmdargs{bisect}{--bad} command. - \end{itemize} -\item The command uses your information to decide which changeset to - test next. -\item It updates the working directory to that changeset, and the - process begins again. -\end{enumerate} -The process ends when \hgcmd{bisect} identifies a unique changeset -that marks the point where your test transitioned from ``succeeding'' -to ``failing''. - -To start the search, we must run the \hgcmdargs{bisect}{--reset} command. -\interaction{bisect.search.init} - -In our case, the binary test we use is simple: we check to see if any -file in the repository contains the string ``i have a gub''. If it -does, this changeset contains the change that ``caused the bug''. By -convention, a changeset that has the property we're searching for is -``bad'', while one that doesn't is ``good''. - -Most of the time, the revision to which the working directory is -synced (usually the tip) already exhibits the problem introduced by -the buggy change, so we'll mark it as ``bad''. -\interaction{bisect.search.bad-init} - -Our next task is to nominate a changeset that we know \emph{doesn't} -have the bug; the \hgcmd{bisect} command will ``bracket'' its search -between the first pair of good and bad changesets. In our case, we -know that revision~10 didn't have the bug. (I'll have more words -about choosing the first ``good'' changeset later.) -\interaction{bisect.search.good-init} - -Notice that this command printed some output. -\begin{itemize} -\item It told us how many changesets it must consider before it can - identify the one that introduced the bug, and how many tests that - will require. -\item It updated the working directory to the next changeset to test, - and told us which changeset it's testing. -\end{itemize} - -We now run our test in the working directory. We use the -\command{grep} command to see if our ``bad'' file is present in the -working directory. If it is, this revision is bad; if not, this -revision is good. -\interaction{bisect.search.step1} - -This test looks like a perfect candidate for automation, so let's turn -it into a shell function. -\interaction{bisect.search.mytest} -We can now run an entire test step with a single command, -\texttt{mytest}. -\interaction{bisect.search.step2} -A few more invocations of our canned test step command, and we're -done. -\interaction{bisect.search.rest} - -Even though we had~40 changesets to search through, the \hgcmd{bisect} -command let us find the changeset that introduced our ``bug'' with -only five tests. Because the number of tests that the \hgcmd{bisect} -command performs grows logarithmically with the number of changesets to -search, the advantage that it has over the ``brute force'' search -approach increases with every changeset you add. - -\subsection{Cleaning up after your search} - -When you're finished using the \hgcmd{bisect} command in a -repository, you can use the \hgcmdargs{bisect}{reset} command to drop -the information it was using to drive your search. The command -doesn't use much space, so it doesn't matter if you forget to run this -command. However, \hgcmd{bisect} won't let you start a new search in -that repository until you do a \hgcmdargs{bisect}{reset}. -\interaction{bisect.search.reset} - -\section{Tips for finding bugs effectively} - -\subsection{Give consistent input} - -The \hgcmd{bisect} command requires that you correctly report the -result of every test you perform. If you tell it that a test failed -when it really succeeded, it \emph{might} be able to detect the -inconsistency. If it can identify an inconsistency in your reports, -it will tell you that a particular changeset is both good and bad. -However, it can't do this perfectly; it's about as likely to report -the wrong changeset as the source of the bug. - -\subsection{Automate as much as possible} - -When I started using the \hgcmd{bisect} command, I tried a few times -to run my tests by hand, on the command line. This is an approach -that I, at least, am not suited to. After a few tries, I found that I -was making enough mistakes that I was having to restart my searches -several times before finally getting correct results. - -My initial problems with driving the \hgcmd{bisect} command by hand -occurred even with simple searches on small repositories; if the -problem you're looking for is more subtle, or the number of tests that -\hgcmd{bisect} must perform increases, the likelihood of operator -error ruining the search is much higher. Once I started automating my -tests, I had much better results. - -The key to automated testing is twofold: -\begin{itemize} -\item always test for the same symptom, and -\item always feed consistent input to the \hgcmd{bisect} command. -\end{itemize} -In my tutorial example above, the \command{grep} command tests for the -symptom, and the \texttt{if} statement takes the result of this check -and ensures that we always feed the same input to the \hgcmd{bisect} -command. The \texttt{mytest} function marries these together in a -reproducible way, so that every test is uniform and consistent. - -\subsection{Check your results} - -Because the output of a \hgcmd{bisect} search is only as good as the -input you give it, don't take the changeset it reports as the -absolute truth. A simple way to cross-check its report is to manually -run your test at each of the following changesets: -\begin{itemize} -\item The changeset that it reports as the first bad revision. Your - test should still report this as bad. -\item The parent of that changeset (either parent, if it's a merge). - Your test should report this changeset as good. -\item A child of that changeset. Your test should report this - changeset as bad. -\end{itemize} - -\subsection{Beware interference between bugs} - -It's possible that your search for one bug could be disrupted by the -presence of another. For example, let's say your software crashes at -revision 100, and worked correctly at revision 50. Unknown to you, -someone else introduced a different crashing bug at revision 60, and -fixed it at revision 80. This could distort your results in one of -several ways. - -It is possible that this other bug completely ``masks'' yours, which -is to say that it occurs before your bug has a chance to manifest -itself. If you can't avoid that other bug (for example, it prevents -your project from building), and so can't tell whether your bug is -present in a particular changeset, the \hgcmd{bisect} command cannot -help you directly. Instead, you can mark a changeset as untested by -running \hgcmdargs{bisect}{--skip}. - -A different problem could arise if your test for a bug's presence is -not specific enough. If you check for ``my program crashes'', then -both your crashing bug and an unrelated crashing bug that masks it -will look like the same thing, and mislead \hgcmd{bisect}. - -Another useful situation in which to use \hgcmdargs{bisect}{--skip} is -if you can't test a revision because your project was in a broken and -hence untestable state at that revision, perhaps because someone -checked in a change that prevented the project from building. - -\subsection{Bracket your search lazily} - -Choosing the first ``good'' and ``bad'' changesets that will mark the -end points of your search is often easy, but it bears a little -discussion nevertheless. From the perspective of \hgcmd{bisect}, the -``newest'' changeset is conventionally ``bad'', and the older -changeset is ``good''. - -If you're having trouble remembering when a suitable ``good'' change -was, so that you can tell \hgcmd{bisect}, you could do worse than -testing changesets at random. Just remember to eliminate contenders -that can't possibly exhibit the bug (perhaps because the feature with -the bug isn't present yet) and those where another problem masks the -bug (as I discussed above). - -Even if you end up ``early'' by thousands of changesets or months of -history, you will only add a handful of tests to the total number that -\hgcmd{bisect} must perform, thanks to its logarithmic behaviour. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/wdir-after-commit.svg --- a/fr/wdir-after-commit.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,394 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - dfbbb33f3fa3 - - - e7639888bb2f - - 7b064d8bac5e - - - - 000000000000 - - History in repository - - - - dfbbb33f3fa3 - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - Newchangeset - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/wdir-branch.svg --- a/fr/wdir-branch.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,418 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - e7639888bb2f - - - - 7b064d8bac5e - - - - 000000000000 - - - - - ffb20e1701ea - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - - - ffb20e1701ea - - - Pre-existing head - Newly created head (and tip) - - - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/wdir-merge.svg --- a/fr/wdir-merge.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,425 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - 7b064d8bac5e - - - - 000000000000 - - - - - ffb20e1701ea - - - - e7639888bb2f - - First parent (unchanged) - Second parent - Parents of working directory - - - - - ffb20e1701ea - - - Pre-existing head - Newly created head (and tip) - - - - - - e7639888bb2f - - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/wdir-pre-branch.svg --- a/fr/wdir-pre-branch.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,364 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - e7639888bb2f - - - 7b064d8bac5e - - - - 000000000000 - - History in repository - - - - 7b064d8bac5e - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - - diff -r 1dd00abb3fa9 -r 6b680d569bb4 fr/wdir.svg --- a/fr/wdir.svg Sun Aug 16 03:41:39 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - e7639888bb2f - - 7b064d8bac5e - - - 000000000000 - - - History in repository - - - - - e7639888bb2f - - - - 000000000000 - - First parent - Second parent - - Parents of working directory - - - -