# HG changeset patch # User Yoshiki Yazawa # Date 1202287391 -32400 # Node ID b0db5adf11c1e096c4b08f42befb8e7e18120ed0 # Parent 7be02466421bcb6d96e6ea60ee4da2e9721b8aaf fork Japanese translation. diff -r 7be02466421b -r b0db5adf11c1 ja/00book.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/00book.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,76 @@ +% 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} +% 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{Distributed revision control with Mercurial} \author{Bryan + O'Sullivan} +\date{Copyright \copyright\ 2006, 2007 Bryan O'Sullivan.\\ + This material may be distributed only subject to the terms and + conditions set forth in version 1.0 of the Open Publication License. + Please refer to Appendix~\ref{cha:opl} for the license text.\\ + This book was prepared from + \href{http://hg.serpentine.com/mercurial/book/}{rev~\input{build_id}} + using \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 7be02466421b -r b0db5adf11c1 ja/99book.bib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/99book.bib Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,76 @@ +@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 7be02466421b -r b0db5adf11c1 ja/99defs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/99defs.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,142 @@ +% 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}} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 7be02466421b -r b0db5adf11c1 ja/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/Makefile Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,216 @@ +# This makefile requires GNU make. + +sources := \ + 00book.tex \ + 99book.bib \ + 99defs.tex \ + build_id.tex \ + branch.tex \ + cmdref.tex \ + collab.tex \ + concepts.tex \ + daily.tex \ + filenames.tex \ + hg_id.tex \ + hgext.tex \ + hook.tex \ + intro.tex \ + mq.tex \ + mq-collab.tex \ + mq-ref.tex \ + preface.tex \ + srcinstall.tex \ + template.tex \ + tour-basic.tex \ + tour-merge.tex \ + undo.tex + +image-sources := \ + feature-branches.dot \ + filelog.svg \ + kdiff3.png \ + metadata.svg \ + mq-stack.svg \ + note.png \ + revlog.svg \ + snapshot.svg \ + tour-history.svg \ + tour-merge-conflict.svg \ + tour-merge-merge.svg \ + tour-merge-pull.svg \ + tour-merge-sep-repos.svg \ + undo-manual.dot \ + undo-manual-merge.dot \ + undo-non-tip.dot \ + undo-simple.dot \ + wdir.svg \ + wdir-after-commit.svg \ + wdir-branch.svg \ + wdir-merge.svg \ + wdir-pre-branch.svg + +image-dot := $(filter %.dot,$(image-sources)) +image-svg := $(filter %.svg,$(image-sources)) +image-png := $(filter %.png,$(image-sources)) + +image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) +image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) + +example-sources := \ + backout \ + bisect \ + branching \ + branch-named \ + branch-repo \ + cmdref \ + daily.copy \ + daily.files \ + daily.rename \ + daily.revert \ + extdiff \ + filenames \ + hook.msglen \ + hook.simple \ + hook.ws \ + issue29 \ + mq.guards \ + mq.qinit-help \ + mq.dodiff \ + mq.id \ + mq.tarball \ + mq.tools \ + mq.tutorial \ + rename.divergent \ + rollback \ + tag \ + template.simple \ + template.svnstyle \ + tour \ + tour-merge-conflict + +example-prereqs := \ + /usr/bin/merge + +dist-sources := \ + ../html/hgicon.png \ + ../html/index.html.var \ + ../html/index.en.html + +latex-options = \ + -interaction batchmode \ + -output-directory $(dir $(1)) \ + -jobname $(basename $(notdir $(1))) + +hg = $(shell which hg) + +hg-id = $(shell hg parents --template '{node|short}, dated {date|isodate},\n') + +hg-version = $(shell hg version -q | \ + sed 's,.*(version \(unknown\|[a-f0-9+]*\)),\1,') + +all: pdf html + +pdf: pdf/hgbook.pdf + +define pdf + mkdir -p $(dir $@) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + cp 99book.bib $(dir $@) + cd $(dir $@) && bibtex $(basename $(notdir $@)) + cd $(dir $@) && makeindex $(basename $(notdir $@)) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + if grep 'Reference.*undefined' $(@:.pdf=.log); then exit 1; fi +endef + +pdf/hgbook.pdf: $(sources) $(image-pdf) examples + $(call pdf) + +html: onepage split + +onepage: $(htlatex) html/onepage/hgbook.html html/onepage/hgbook.css $(image-html:%=html/onepage/%) + +html/onepage/%: % + cp $< $@ + +split: $(htlatex) html/split/hgbook.html html/split/hgbook.css $(image-html:%=html/split/%) + +html/split/%: % + cp $< $@ + +# This is a horrible hack to work around the fact that the htlatex +# command in tex4ht is itself a horrible hack. I really don't want to +# include verbatim the big wad of TeX that is repeated in that script, +# but I've given up and run a hacked copy as htlatex.book here. + +define htlatex + mkdir -p $(dir $(1)) + cp 99book.bib $(dir $(1)) + TEXINPUTS=$(dir $(2)): ./htlatex.book $(2) "bookhtml,html4-uni,$(3)" " -cunihtf -utf8" "$(dir $(1))" "$(call latex-options,$(1))" || (rm -f $(1); exit 1) + cd $(dir $(1)) && tex4ht -f/$(basename $(notdir $(1))) -cvalidate -cunihtf + cd $(dir $(1)) && t4ht -f/$(basename $(notdir $(1))) + ./fixhtml.py $(dir $(1))/*.html + rm $(dir $(1))/hgbook.css +endef + +html/onepage/hgbook.html: $(sources) $(image-html) examples bookhtml.cfg + $(call htlatex,$@,$<) + +html/split/hgbook.html: $(sources) examples bookhtml.cfg + $(call htlatex,$@,$<,2) + +# Produce 90dpi PNGs for the web. + +%.png: %.svg + inkscape -D -e $@ $< + +%.svg: %.dot + dot -Tsvg -o $@ $< + +# Produce eps & pdf for the pdf + +%.pdf: %.eps + epstopdf $< + +%.eps: %.svg + inkscape -E $@ $< + +%.eps: %.dot + dot -Tps -o $@ $< + +examples: $(example-prereqs) examples/.run + +examples/.run: $(example-sources:%=examples/%.run) + touch examples/.run + +examples/%.run: examples/% examples/run-example + cd examples && ./run-example $(notdir $<) + +changelog := $(wildcard ../.hg/store/00changelog.[id]) +ifeq ($(changelog),) +changelog := $(wildcard ../.hg/00changelog.[id]) +endif + +build_id.tex: $(changelog) + echo -n '$(hg-id)' > build_id.tex + +hg_id.tex: $(hg) + echo -n '$(hg-version)' > hg_id.tex + +clean: + rm -rf dist html pdf \ + $(image-dot:%.dot=%.pdf) \ + $(image-dot:%.dot=%.png) \ + $(image-svg:%.svg=%.pdf) \ + $(image-svg:%.svg=%.png) \ + examples/*.{lxo,run} examples/.run build_id.tex hg_id.tex + +install: pdf split $(dist-sources) + rm -rf dist + mkdir -p dist + cp pdf/hgbook.pdf dist + cp html/split/*.{css,html,png} dist + cp $(dist-sources) dist + +rsync: install + rsync -avz --delete dist sp.red-bean.com:public_html/hgbook diff -r 7be02466421b -r b0db5adf11c1 ja/bookhtml.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/bookhtml.cfg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,18 @@ +% -*- 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{}\ShowPar} + {\endItem \def\endItem{\EndP\Tg}\HCode{
  • }} + {\HCode{}} +\def\textbullet{} + +\begin{document} + +\EndPreamble diff -r 7be02466421b -r b0db5adf11c1 ja/branch.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/branch.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,396 @@ +\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} +We can no longer update from \texttt{foo} to \texttt{bar} without +going ``sideways'' in history, so Mercurial forces us to provide the +\hgopt{update}{-C} option to \hgcmd{update}. +\interaction{branch-named.update-bar} + +\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 7be02466421b -r b0db5adf11c1 ja/cmdref.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/cmdref.py Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,156 @@ +#!/usr/bin/env python + +import getopt +import itertools +import os +import re +import sys + +def usage(exitcode): + print >> sys.stderr, ('usage: %s [-H|--hidden] hg_repo' % + os.path.basename(sys.argv[0])) + sys.exit(exitcode) + +try: + opts, args = getopt.getopt(sys.argv[1:], 'AHh?', ['all', 'help', 'hidden']) + opt_all = False + opt_hidden = False + for o, a in opts: + if o in ('-h', '-?', '--help'): + usage(0) + if o in ('-A', '--all'): + opt_all = True + if o in ('-H', '--hidden'): + opt_hidden = True +except getopt.GetoptError, err: + print >> sys.stderr, 'error:', err + usage(1) + +try: + hg_repo, ltx_file = args +except ValueError: + usage(1) + +if not os.path.isfile(os.path.join(hg_repo, 'mercurial', 'commands.py')): + print >> sys.stderr, ('error: %r does not contain mercurial code' % + hg_repo) + sys.exit(1) + +sys.path.insert(0, hg_repo) + +from mercurial import commands + +def get_commands(): + seen = {} + for name, info in sorted(commands.table.iteritems()): + aliases = name.split('|', 1) + name = aliases.pop(0).lstrip('^') + function, options, synopsis = info + seen[name] = {} + for shortopt, longopt, arg, desc in options: + seen[name][longopt] = shortopt + return seen + +def cmd_filter((name, aliases, options)): + if opt_all: + return True + if opt_hidden: + return name.startswith('debug') + return not name.startswith('debug') + +def scan(ltx_file): + cmdref_re = re.compile(r'^\\cmdref{(?P\w+)}') + optref_re = re.compile(r'^\\l?optref{(?P\w+)}' + r'(?:{(?P[^}])})?' + r'{(?P[^}]+)}') + + seen = {} + locs = {} + for lnum, line in enumerate(open(ltx_file)): + m = cmdref_re.match(line) + if m: + d = m.groupdict() + cmd = d['cmd'] + seen[cmd] = {} + locs[cmd] = lnum + 1 + continue + m = optref_re.match(line) + if m: + d = m.groupdict() + seen[d['cmd']][d['long']] = d['short'] + continue + return seen, locs + +documented, locs = scan(ltx_file) +known = get_commands() + +doc_set = set(documented) +known_set = set(known) + +errors = 0 + +for nonexistent in sorted(doc_set.difference(known_set)): + print >> sys.stderr, ('%s:%d: %r command does not exist' % + (ltx_file, locs[nonexistent], nonexistent)) + errors += 1 + +def optcmp(a, b): + la, sa = a + lb, sb = b + sc = cmp(sa, sb) + if sc: + return sc + return cmp(la, lb) + +for cmd in doc_set.intersection(known_set): + doc_opts = documented[cmd] + known_opts = known[cmd] + + do_set = set(doc_opts) + ko_set = set(known_opts) + + for nonexistent in sorted(do_set.difference(ko_set)): + print >> sys.stderr, ('%s:%d: %r option to %r command does not exist' % + (ltx_file, locs[cmd], nonexistent, cmd)) + errors += 1 + + def mycmp(la, lb): + sa = known_opts[la] + sb = known_opts[lb] + return optcmp((la, sa), (lb, sb)) + + for undocumented in sorted(ko_set.difference(do_set), cmp=mycmp): + print >> sys.stderr, ('%s:%d: %r option to %r command not documented' % + (ltx_file, locs[cmd], undocumented, cmd)) + shortopt = known_opts[undocumented] + if shortopt: + print '\optref{%s}{%s}{%s}' % (cmd, shortopt, undocumented) + else: + print '\loptref{%s}{%s}' % (cmd, undocumented) + errors += 1 + sys.stdout.flush() + +if errors: + sys.exit(1) + +sorted_locs = sorted(locs.iteritems(), key=lambda x:x[1]) + +def next_loc(cmd): + for i, (name, loc) in enumerate(sorted_locs): + if name >= cmd: + return sorted_locs[i-1][1] + 1 + return loc + +for undocumented in sorted(known_set.difference(doc_set)): + print >> sys.stderr, ('%s:%d: %r command not documented' % + (ltx_file, next_loc(undocumented), undocumented)) + print '\cmdref{%s}' % undocumented + for longopt, shortopt in sorted(known[undocumented].items(), cmp=optcmp): + if shortopt: + print '\optref{%s}{%s}{%s}' % (undocumented, shortopt, longopt) + else: + print '\loptref{%s}{%s}' % (undocumented, longopt) + sys.stdout.flush() + errors += 1 + +sys.exit(errors and 1 or 0) diff -r 7be02466421b -r b0db5adf11c1 ja/cmdref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/cmdref.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,176 @@ +\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 7be02466421b -r b0db5adf11c1 ja/collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/collab.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1118 @@ +\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 choose 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 id 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 7be02466421b -r b0db5adf11c1 ja/concepts.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/concepts.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,577 @@ +\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 werea 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 7be02466421b -r b0db5adf11c1 ja/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/daily.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,381 @@ +\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 7be02466421b -r b0db5adf11c1 ja/examples/backout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,83 @@ +#!/bin/bash + +# We have to fake the merges here, because they cause conflicts with +# three-way command-line merge, and kdiff3 may not be available. + +export HGMERGE=$(mktemp) +echo '#!/bin/sh' >> $HGMERGE +echo 'echo first change > "$1"' >> $HGMERGE +echo 'echo third change >> "$1"' >> $HGMERGE +chmod 700 $HGMERGE + +#$ name: init + +hg init myrepo +cd myrepo +echo first change >> myfile +hg add myfile +hg commit -m 'first change' +echo second change >> myfile +hg commit -m 'second change' + +#$ name: simple + +hg backout -m 'back out second change' tip +cat myfile + +#$ name: simple.log +#$ ignore: \s+200[78]-.* + +hg log --style compact + +#$ name: non-tip.clone + +cd .. +hg clone -r1 myrepo non-tip-repo +cd non-tip-repo + +#$ name: non-tip.backout + +echo third change >> myfile +hg commit -m 'third change' +hg backout --merge -m 'back out second change' 1 + +#$ name: non-tip.cat +cat myfile + +#$ name: manual.clone + +cd .. +hg clone -r1 myrepo newrepo +cd newrepo + +#$ name: manual.backout + +echo third change >> myfile +hg commit -m 'third change' +hg backout -m 'back out second change' 1 + +#$ name: manual.log + +hg log --style compact + +#$ name: manual.parents + +hg parents + +#$ name: manual.heads + +hg heads + +#$ name: manual.cat + +cat myfile + +#$ name: manual.merge + +hg merge +hg commit -m 'merged backout with previous tip' +cat myfile + +#$ name: + +rm $HGMERGE diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init myrepo} +$ \textbf{cd myrepo} +$ \textbf{echo first change >> myfile} +$ \textbf{hg add myfile} +$ \textbf{hg commit -m 'first change'} +$ \textbf{echo second change >> myfile} +$ \textbf{hg commit -m 'second change'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.backout.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.backout.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{echo third change >> myfile} +$ \textbf{hg commit -m 'third change'} +$ \textbf{hg backout -m 'back out second change' 1} +reverting myfile +changeset backs out changeset +the backout changeset is a new head - do not forget to merge +(use "backout --merge" if you want to auto-merge) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.cat.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat myfile} +first change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -r1 myrepo newrepo} +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd newrepo} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.heads.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.heads.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg heads} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: back out second change + +changeset: +user: Bryan O'Sullivan + +summary: third change + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.log.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg log --style compact} +3[tip]:1 + back out second change + +2 + third change + +1 + second change + +0 + first change + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg merge} +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'merged backout with previous tip'} +$ \textbf{cat myfile} +first change +third change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.manual.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.parents.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg parents} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: back out second change + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.non-tip.backout.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.backout.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{echo third change >> myfile} +$ \textbf{hg commit -m 'third change'} +$ \textbf{hg backout --merge -m 'back out second change' 1} +reverting myfile +changeset backs out changeset +merging with changeset +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.non-tip.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.cat.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat myfile} +first change +third change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.non-tip.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -r1 myrepo non-tip-repo} +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd non-tip-repo} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.simple.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.simple.log.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg log --style compact} +2[tip] + back out second change + +1 + second change + +0 + first change + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/backout.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.simple.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg backout -m 'back out second change' tip} +reverting myfile +changeset backs out changeset +$ \textbf{cat myfile} +first change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,88 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hbisect =' >> $HGRC + +# XXX There's some kind of horrible nondeterminism in the execution of +# bisect at the moment. Ugh. + +#$ ignore: .* + +#$ name: init + +hg init mybug +cd mybug + +#$ name: commits + +buggy_change=22 + +for (( i = 0; i < 35; i++ )); do + if [[ $i = $buggy_change ]]; then + echo 'i have a gub' > myfile$i + hg commit -q -A -m 'buggy changeset' + else + echo 'nothing to see here, move along' > myfile$i + hg commit -q -A -m 'normal changeset' + fi +done + +#$ name: help + +hg help bisect + +#$ name: search.init + +hg bisect --init + +#$ name: search.bad-init + +hg bisect --bad + +#$ name: search.good-init + +hg bisect --good 10 + +#$ name: search.step1 + +if grep -q 'i have a gub' * +then + result=bad +else + result=good +fi + +echo this revision is $result +hg bisect --$result + +#$ name: search.mytest + +mytest() { + if grep -q 'i have a gub' * + then + result=bad + else + result=good + fi + + echo this revision is $result + hg bisect --$result +} + +#$ name: search.step2 + +mytest + +#$ name: search.rest + +mytest +mytest +mytest + +#$ name: search.reset + +hg bisect --reset + +#$ name: + +exit 0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.commits.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.commits.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.help.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.bad-init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.bad-init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.good-init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.good-init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.mytest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.mytest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.reset.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.reset.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.rest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.rest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.step1.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.step1.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/bisect.search.step2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.step2.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +hg init a +cd a +echo hello > myfile +hg commit -A -m 'Initial commit' + +#$ name: branches + +hg tip +hg branches + +#$ name: branch + +hg branch + +#$ name: create + +hg branch foo +hg branch + +#$ name: status + +hg status +hg tip + +#$ name: commit + +echo 'hello again' >> myfile +hg commit -m 'Second commit' +hg tip + +#$ name: rebranch + +hg branch +hg branch bar +echo new file > newfile +hg commit -A -m 'Third commit' +hg tip + +#$ name: parents + +hg parents +hg branches + +#$ name: update-switchy + +hg update foo +hg parents +hg update bar +hg parents + +#$ name: update-nothing + +hg update foo +hg update + +#$ name: foo-commit + +echo something > somefile +hg commit -A -m 'New file' +hg heads + +#$ name: update-bar + +hg update bar +hg update -C bar + +#$ name: merge + +hg branch +hg merge +hg commit -m 'Merge' +hg tip diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.branch.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.branch.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg branch} +default diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.branches.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.branches.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Initial commit + +$ \textbf{hg branches} +default diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.commit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{echo 'hello again' >> myfile} +$ \textbf{hg commit -m 'Second commit'} +$ \textbf{hg tip} +changeset: +branch: foo +tag: tip +user: Bryan O'Sullivan + +summary: Second commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.create.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.create.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg branch foo} +marked working directory as branch foo +$ \textbf{hg branch} +foo diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.foo-commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.foo-commit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,18 @@ +$ \textbf{echo something > somefile} +$ \textbf{hg commit -A -m 'New file'} +adding somefile +$ \textbf{hg heads} +changeset: +branch: foo +tag: tip +parent: +user: Bryan O'Sullivan + +summary: New file + +changeset: +branch: bar +user: Bryan O'Sullivan + +summary: Third commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg branch} +bar +$ \textbf{hg merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Merge'} +$ \textbf{hg tip} +changeset: +branch: bar +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Merge + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.parents.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg parents} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + +$ \textbf{hg branches} +bar +foo (inactive) +default (inactive) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.rebranch.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.rebranch.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,15 @@ +$ \textbf{hg branch} +foo +$ \textbf{hg branch bar} +marked working directory as branch bar +$ \textbf{echo new file > newfile} +$ \textbf{hg commit -A -m 'Third commit'} +adding newfile +$ \textbf{hg tip} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg status} +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Initial commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.update-bar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-bar.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg update bar} +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes +$ \textbf{hg update -C bar} +1 files updated, 0 files merged, 1 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.update-foo.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-foo.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.update-nothing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-nothing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.update-switchy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-switchy.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,19 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-named.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parent} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,48 @@ +#!/bin/bash + +hg init myproject +cd myproject +echo hello > myfile +hg commit -A -m 'Initial commit' +cd .. + +#$ name: tag + +cd myproject +hg tag v1.0 + +#$ name: clone + +cd .. +hg clone myproject myproject-1.0.1 + +#$ name: bugfix + +hg clone myproject-1.0.1 my-1.0.1-bugfix +cd my-1.0.1-bugfix +echo 'I fixed a bug using only echo!' >> myfile +hg commit -m 'Important fix for 1.0.1' +#$ ignore: /tmp/branch-repo.* +hg push + +#$ name: new + +cd .. +hg clone myproject my-feature +cd my-feature +echo 'This sure is an exciting new feature!' > mynewfile +hg commit -A -m 'New feature' +hg push + +#$ name: pull + +cd .. +hg clone myproject myproject-merge +cd myproject-merge +hg pull ../myproject-1.0.1 + +#$ name: merge + +hg merge +hg commit -m 'Merge bugfix from 1.0.1 branch' +hg push diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.bugfix.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.bugfix.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg clone myproject-1.0.1 my-1.0.1-bugfix} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-1.0.1-bugfix} +$ \textbf{echo 'I fixed a bug using only echo!' >> myfile} +$ \textbf{hg commit -m 'Important fix for 1.0.1'} +$ \textbf{hg push} +pushing to /tmp/branch-repo4rF-PL/myproject-1.0.1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject myproject-1.0.1} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Merge bugfix from 1.0.1 branch'} +$ \textbf{hg push} +pushing to +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 1 changes to 1 files diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.new.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.new.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,14 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject my-feature} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-feature} +$ \textbf{echo 'This sure is an exciting new feature!' > mynewfile} +$ \textbf{hg commit -A -m 'New feature'} +adding mynewfile +$ \textbf{hg push} +pushing to +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.pull.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject myproject-merge} +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd myproject-merge} +$ \textbf{hg pull ../myproject-1.0.1} +pulling from ../myproject-1.0.1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branch-repo.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.tag.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cd myproject} +$ \textbf{hg tag v1.0} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,63 @@ +#!/bin/bash + +#$ name: init + +hg init main +cd main +echo 'This is a boring feature.' > myfile +hg commit -A -m 'We have reached an important milestone!' + +#$ name: tag + +hg tag v1.0 +hg tip +hg tags + +#$ name: main + +cd ../main +echo 'This is exciting and new!' >> myfile +hg commit -m 'Add a new feature' +cat myfile + +#$ name: update + +cd .. +hg clone -U main main-old +cd main-old +hg update v1.0 +cat myfile + +#$ name: clone + +cd .. +hg clone -rv1.0 main stable + +#$ name: stable + +hg clone stable stable-fix +cd stable-fix +echo 'This is a fix to a boring feature.' > myfile +hg commit -m 'Fix a bug' +#$ ignore: /tmp/branching.* +hg push + +#$ name: + +export HGMERGE=$(mktemp) +echo '#!/bin/sh' > $HGMERGE +echo 'echo "This is a fix to a boring feature." > "$1"' >> $HGMERGE +echo 'echo "This is exciting and new!" >> "$1"' >> $HGMERGE +chmod 700 $HGMERGE + +#$ name: merge + +cd ../main +hg pull ../stable +hg merge +hg commit -m 'Bring in bugfix from stable branch' +cat myfile + +#$ name: + +rm $HGMERGE diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -rv1.0 main stable} +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init main} +$ \textbf{cd main} +$ \textbf{echo 'This is a boring feature.' > myfile} +$ \textbf{hg commit -A -m 'We have reached an important milestone!'} +adding myfile diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.main.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.main.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cd ../main} +$ \textbf{echo 'This is exciting and new!' >> myfile} +$ \textbf{hg commit -m 'Add a new feature'} +$ \textbf{cat myfile} +This is a boring feature. +This is exciting and new! diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,17 @@ +$ \textbf{cd ../main} +$ \textbf{hg pull ../stable} +pulling from ../stable +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Bring in bugfix from stable branch'} +$ \textbf{cat myfile} +This is a fix to a boring feature. +This is exciting and new! diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.stable.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.stable.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg clone stable stable-fix} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd stable-fix} +$ \textbf{echo 'This is a fix to a boring feature.' > myfile} +$ \textbf{hg commit -m 'Fix a bug'} +$ \textbf{hg push} +pushing to /tmp/branchingfJgZac/stable +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.tag.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg tag v1.0} +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.0 for changeset + +$ \textbf{hg tags} +tip +v1.0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/branching.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.update.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -U main main-old} +$ \textbf{cd main-old} +$ \textbf{hg update v1.0} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cat myfile} +This is a boring feature. diff -r 7be02466421b -r b0db5adf11c1 ja/examples/cmdref --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/cmdref Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ +#!/bin/bash + +hg init diff +cd diff +cat > myfile.c <> $HGRC +echo 'showfunc = False' >> $HGRC + +hg diff + +hg diff -p diff -r 7be02466421b -r b0db5adf11c1 ja/examples/cmdref.diff-p.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/cmdref.diff-p.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ +$ \textbf{echo '[diff]' >> $HGRC} +$ \textbf{echo 'showfunc = False' >> $HGRC} +$ \textbf{hg diff} +diff -r myfile.c + + +@@ -1,4 +1,4 @@ + int myfunc() + \{ +- return 1; ++ return 10; + \} +$ \textbf{hg diff -p} +diff -r myfile.c + + +@@ -1,4 +1,4 @@ int myfunc() + int myfunc() + \{ +- return 1; ++ return 10; + \} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,82 @@ +#!/bin/bash + +#$ name: init + +hg init my-copy +cd my-copy +echo line > file +hg add file +hg commit -m 'Added a file' + +#$ name: clone + +cd .. +hg clone my-copy your-copy + +#$ name: copy + +cd my-copy +hg copy file new-file + +#$ name: status + +hg status + +#$ name: status-copy + +hg status -C +hg commit -m 'Copied file' + +#$ name: other + +cd ../your-copy +echo 'new contents' >> file +hg commit -m 'Changed file' + +#$ name: cat + +cat file +cat ../my-copy/new-file + +#$ name: merge + +hg pull ../my-copy +hg merge +cat new-file + +#$ name: + +cd .. +hg init copy-example +cd copy-example +echo a > a +echo b > b +mkdir c +mkdir c/a +echo c > c/a/c +hg ci -Ama + +#$ name: simple + +mkdir k +hg copy a k +ls k + +#$ name: dir-dest + +mkdir d +hg copy a b d +ls d + +#$ name: dir-src + +hg copy c e + +#$ name: dir-src-dest + +hg copy c d + +#$ name: after + +cp a z +hg copy --after a z diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.after.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.after.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cp a z} +$ \textbf{hg copy --after a z} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.cat.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{cat file} +line +new contents +$ \textbf{cat ../my-copy/new-file} +line diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone my-copy your-copy} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.copy.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cd my-copy} +$ \textbf{hg copy file new-file} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.dir-dest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-dest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{mkdir d} +$ \textbf{hg copy a b d} +$ \textbf{ls d} +a b diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.dir-src-dest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-src-dest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg copy c d} +copying c/a/c to d/c/a/c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.dir-src.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-src.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg copy c e} +copying c/a/c to e/a/c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init my-copy} +$ \textbf{cd my-copy} +$ \textbf{echo line > file} +$ \textbf{hg add file} +$ \textbf{hg commit -m 'Added a file'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,15 @@ +$ \textbf{hg pull ../my-copy} +pulling from ../my-copy +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +merging file and new-file +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{cat new-file} +line +new contents diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.other.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.other.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ../your-copy} +$ \textbf{echo 'new contents' >> file} +$ \textbf{hg commit -m 'Changed file'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.simple.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{mkdir k} +$ \textbf{hg copy a k} +$ \textbf{ls k} +a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.status-copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.status-copy.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status -C} +A new-file + file +$ \textbf{hg commit -m 'Copied file'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.copy.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status} +A new-file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,93 @@ +#!/bin/bash + +#$ name: add + +hg init add-example +cd add-example +echo a > a +hg status +hg add a +hg status +hg commit -m 'Added one file' +hg status + +#$ name: add-dir + +mkdir b +echo b > b/b +echo c > b/c +mkdir b/d +echo d > b/d/d +hg add b +hg commit -m 'Added all files in subdirectory' + +#$ name: + +cd .. + +#$ name: hidden + +hg init hidden-example +cd hidden-example +mkdir empty +touch empty/.hidden +hg add empty/.hidden +hg commit -m 'Manage an empty-looking directory' +ls empty +cd .. +hg clone hidden-example tmp +ls tmp +ls tmp/empty + +#$ name: remove + +hg init remove-example +cd remove-example +echo a > a +mkdir b +echo b > b/b +hg add a b +hg commit -m 'Small example for file removal' +hg remove a +hg status +hg remove b + +#$ name: + +cd .. + +#$ name: missing +hg init missing-example +cd missing-example +echo a > a +hg add a +hg commit -m 'File about to be missing' +rm a +hg status + +#$ name: remove-after + +hg remove --after a +hg status + +#$ name: recover-missing +hg revert a +cat a +hg status + +#$ name: + +cd .. + +#$ name: addremove + +hg init addremove-example +cd addremove-example +echo a > a +echo b > b +hg addremove + +#$ name: commit-addremove + +echo c > c +hg commit -A -m 'Commit with addremove' diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.add-dir.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.add-dir.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{echo c > b/c} +$ \textbf{mkdir b/d} +$ \textbf{echo d > b/d/d} +$ \textbf{hg add b} +adding b/b +adding b/c +adding b/d/d +$ \textbf{hg commit -m 'Added all files in subdirectory'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.add.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg init add-example} +$ \textbf{cd add-example} +$ \textbf{echo a > a} +$ \textbf{hg status} +? a +$ \textbf{hg add a} +$ \textbf{hg status} +A a +$ \textbf{hg commit -m 'Added one file'} +$ \textbf{hg status} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.addremove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.addremove.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init addremove-example} +$ \textbf{cd addremove-example} +$ \textbf{echo a > a} +$ \textbf{echo b > b} +$ \textbf{hg addremove} +adding a +adding b diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.commit-addremove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.commit-addremove.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{echo c > c} +$ \textbf{hg commit -A -m 'Commit with addremove'} +adding c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.hidden.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.hidden.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg init hidden-example} +$ \textbf{cd hidden-example} +$ \textbf{mkdir empty} +$ \textbf{touch empty/.hidden} +$ \textbf{hg add empty/.hidden} +$ \textbf{hg commit -m 'Manage an empty-looking directory'} +$ \textbf{ls empty} +$ \textbf{cd ..} +$ \textbf{hg clone hidden-example tmp} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{ls tmp} +empty +$ \textbf{ls tmp/empty} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.missing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg init missing-example} +$ \textbf{cd missing-example} +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -m 'File about to be missing'} +$ \textbf{rm a} +$ \textbf{hg status} +! a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.recover-missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.recover-missing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg revert a} +$ \textbf{cat a} +a +$ \textbf{hg status} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.remove-after.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.remove-after.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg remove --after a} +$ \textbf{hg status} +R a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.files.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.remove.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg init remove-example} +$ \textbf{cd remove-example} +$ \textbf{echo a > a} +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{hg add a b} +adding b/b +$ \textbf{hg commit -m 'Small example for file removal'} +$ \textbf{hg remove a} +$ \textbf{hg status} +R a +$ \textbf{hg remove b} +removing b/b diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.rename --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,18 @@ +#!/bin/bash + +hg init a +cd a +echo a > a +hg ci -Ama + +#$ name: rename + +hg rename a b + +#$ name: status + +hg status + +#$ name: status-copy + +hg status -C diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.rename.rename.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.rename.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg rename a b} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.rename.status-copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.status-copy.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status -C} +A b + a +R a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.rename.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status} +A b +R a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +hg init a +cd a +echo 'original content' > file +hg ci -Ama + +#$ name: modify + +cat file +echo unwanted change >> file +hg diff file + +#$ name: unmodify + +hg status +hg revert file +cat file + +#$ name: status + +hg status +cat file.orig + +#$ name: + +rm file.orig + +#$ name: add + +echo oops > oops +hg add oops +hg status oops +hg revert oops +hg status + +#$ name: + +rm oops + +#$ name: remove + +hg remove file +hg status +hg revert file +hg status +ls file + +#$ name: missing + +rm file +hg status +hg revert file +ls file + +#$ name: copy + +hg copy file new-file +hg revert new-file +hg status + +#$ name: + +rm new-file + +#$ name: rename + +hg rename file new-file +hg revert new-file +hg status + +#$ name: rename-orig +hg revert file +hg status diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.add.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{echo oops > oops} +$ \textbf{hg add oops} +$ \textbf{hg status oops} +A oops +$ \textbf{hg revert oops} +$ \textbf{hg status} +? oops diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.copy.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg copy file new-file} +$ \textbf{hg revert new-file} +$ \textbf{hg status} +? new-file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.missing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{rm file} +$ \textbf{hg status} +! file +$ \textbf{hg revert file} +$ \textbf{ls file} +file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.modify.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.modify.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat file} +original content +$ \textbf{echo unwanted change >> file} +$ \textbf{hg diff file} +diff -r file + + +@@ -1,1 +1,2 @@ original content + original content ++unwanted change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.remove.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg remove file} +$ \textbf{hg status} +R file +$ \textbf{hg revert file} +$ \textbf{hg status} +$ \textbf{ls file} +file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.rename-orig.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.rename-orig.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg revert file} +no changes needed to file +$ \textbf{hg status} +? new-file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.rename.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.rename.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg rename file new-file} +$ \textbf{hg revert new-file} +$ \textbf{hg status} +? new-file diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status} +? file.orig +$ \textbf{cat file.orig} +original content +unwanted change diff -r 7be02466421b -r b0db5adf11c1 ja/examples/daily.revert.unmodify.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.unmodify.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status} +M file +$ \textbf{hg revert file} +$ \textbf{cat file} +original content diff -r 7be02466421b -r b0db5adf11c1 ja/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/data/check_whitespace.py Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,44 @@ +#!/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 7be02466421b -r b0db5adf11c1 ja/examples/data/netplug-1.2.5.tar.bz2 Binary file ja/examples/data/netplug-1.2.5.tar.bz2 has changed diff -r 7be02466421b -r b0db5adf11c1 ja/examples/data/netplug-1.2.8.tar.bz2 Binary file ja/examples/data/netplug-1.2.8.tar.bz2 has changed diff -r 7be02466421b -r b0db5adf11c1 ja/examples/data/remove-redundant-null-checks.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/data/remove-redundant-null-checks.patch Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,190 @@ + +From: Jesper Juhl + +Remove redundant NULL chck before kfree + tiny CodingStyle cleanup for +drivers/ + +Signed-off-by: Jesper Juhl +Signed-off-by: Andrew Morton +--- + + drivers/char/agp/sgi-agp.c | 5 ++--- + drivers/char/hvcs.c | 11 +++++------ + drivers/message/fusion/mptfc.c | 6 ++---- + drivers/message/fusion/mptsas.c | 3 +-- + drivers/net/fs_enet/fs_enet-mii.c | 3 +-- + drivers/net/wireless/ipw2200.c | 22 ++++++---------------- + drivers/scsi/libata-scsi.c | 4 +--- + drivers/video/au1100fb.c | 3 +-- + 8 files changed, 19 insertions(+), 38 deletions(-) + +diff -puN drivers/char/agp/sgi-agp.c~remove-redundant-null-checks-before-free-in-drivers drivers/char/agp/sgi-agp.c +--- a/drivers/char/agp/sgi-agp.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/char/agp/sgi-agp.c +@@ -329,9 +329,8 @@ static int __devinit agp_sgi_init(void) + + static void __devexit agp_sgi_cleanup(void) + { +- if (sgi_tioca_agp_bridges) +- kfree(sgi_tioca_agp_bridges); +- sgi_tioca_agp_bridges=NULL; ++ kfree(sgi_tioca_agp_bridges); ++ sgi_tioca_agp_bridges = NULL; + } + + module_init(agp_sgi_init); +diff -puN drivers/char/hvcs.c~remove-redundant-null-checks-before-free-in-drivers drivers/char/hvcs.c +--- a/drivers/char/hvcs.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/char/hvcs.c +@@ -1320,11 +1320,12 @@ static struct tty_operations hvcs_ops = + static int hvcs_alloc_index_list(int n) + { + int i; ++ + hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); + if (!hvcs_index_list) + return -ENOMEM; + hvcs_index_count = n; +- for(i = 0; i < hvcs_index_count; i++) ++ for (i = 0; i < hvcs_index_count; i++) + hvcs_index_list[i] = -1; + return 0; + } +@@ -1332,11 +1333,9 @@ static int hvcs_alloc_index_list(int n) + static void hvcs_free_index_list(void) + { + /* Paranoia check to be thorough. */ +- if (hvcs_index_list) { +- kfree(hvcs_index_list); +- hvcs_index_list = NULL; +- hvcs_index_count = 0; +- } ++ kfree(hvcs_index_list); ++ hvcs_index_list = NULL; ++ hvcs_index_count = 0; + } + + static int __init hvcs_module_init(void) +diff -puN drivers/message/fusion/mptfc.c~remove-redundant-null-checks-before-free-in-drivers drivers/message/fusion/mptfc.c +--- a/drivers/message/fusion/mptfc.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/message/fusion/mptfc.c +@@ -305,10 +305,8 @@ mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in + } + + out: +- if (pp0_array) +- kfree(pp0_array); +- if (p0_array) +- kfree(p0_array); ++ kfree(pp0_array); ++ kfree(p0_array); + return rc; + } + +diff -puN drivers/message/fusion/mptsas.c~remove-redundant-null-checks-before-free-in-drivers drivers/message/fusion/mptsas.c +--- a/drivers/message/fusion/mptsas.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/message/fusion/mptsas.c +@@ -1378,8 +1378,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc) + return 0; + + out_free_port_info: +- if (hba) +- kfree(hba); ++ kfree(hba); + out: + return error; + } +diff -puN drivers/net/fs_enet/fs_enet-mii.c~remove-redundant-null-checks-before-free-in-drivers drivers/net/fs_enet/fs_enet-mii.c +--- a/drivers/net/fs_enet/fs_enet-mii.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/net/fs_enet/fs_enet-mii.c +@@ -431,8 +431,7 @@ static struct fs_enet_mii_bus *create_bu + return bus; + + err: +- if (bus) +- kfree(bus); ++ kfree(bus); + return ERR_PTR(ret); + } + +diff -puN drivers/net/wireless/ipw2200.c~remove-redundant-null-checks-before-free-in-drivers drivers/net/wireless/ipw2200.c +--- a/drivers/net/wireless/ipw2200.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/net/wireless/ipw2200.c +@@ -1229,12 +1229,6 @@ static struct ipw_fw_error *ipw_alloc_er + return error; + } + +-static void ipw_free_error_log(struct ipw_fw_error *error) +-{ +- if (error) +- kfree(error); +-} +- + static ssize_t show_event_log(struct device *d, + struct device_attribute *attr, char *buf) + { +@@ -1296,10 +1290,9 @@ static ssize_t clear_error(struct device + const char *buf, size_t count) + { + struct ipw_priv *priv = dev_get_drvdata(d); +- if (priv->error) { +- ipw_free_error_log(priv->error); +- priv->error = NULL; +- } ++ ++ kfree(priv->error); ++ priv->error = NULL; + return count; + } + +@@ -1970,8 +1963,7 @@ static void ipw_irq_tasklet(struct ipw_p + struct ipw_fw_error *error = + ipw_alloc_error_log(priv); + ipw_dump_error_log(priv, error); +- if (error) +- ipw_free_error_log(error); ++ kfree(error); + } + #endif + } else { +@@ -11693,10 +11685,8 @@ static void ipw_pci_remove(struct pci_de + } + } + +- if (priv->error) { +- ipw_free_error_log(priv->error); +- priv->error = NULL; +- } ++ kfree(priv->error); ++ priv->error = NULL; + + #ifdef CONFIG_IPW2200_PROMISCUOUS + ipw_prom_free(priv); +diff -puN drivers/scsi/libata-scsi.c~remove-redundant-null-checks-before-free-in-drivers drivers/scsi/libata-scsi.c +--- a/drivers/scsi/libata-scsi.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/scsi/libata-scsi.c +@@ -222,9 +222,7 @@ int ata_cmd_ioctl(struct scsi_device *sc + && copy_to_user(arg + sizeof(args), argbuf, argsize)) + rc = -EFAULT; + error: +- if (argbuf) +- kfree(argbuf); +- ++ kfree(argbuf); + return rc; + } + +diff -puN drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers drivers/video/au1100fb.c +--- a/drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/video/au1100fb.c +@@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) + { + driver_unregister(&au1100fb_driver); + +- if (drv_info.opt_mode) +- kfree(drv_info.opt_mode); ++ kfree(drv_info.opt_mode); + } + + module_init(au1100fb_init); +_ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/extdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'extdiff =' >> $HGRC + +hg init a +cd a +echo 'The first line.' > myfile +hg ci -Ama +echo 'The second line.' >> myfile + +#$ name: diff + +hg diff + +#$ name: extdiff + +hg extdiff + +#$ name: extdiff-ctx + +#$ ignore: ^\*\*\* a.* + +hg extdiff -o -NprcC5 + +#$ name: + +exit 0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/extdiff.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.diff.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg diff} +diff -r myfile + + +@@ -1,1 +1,2 @@ The first line. + The first line. ++The second line. diff -r 7be02466421b -r b0db5adf11c1 ja/examples/extdiff.extdiff-ctx.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.extdiff-ctx.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg extdiff -o -NprcC5} + + +*************** +*** 1 **** + + The first line. ++ The second line. diff -r 7be02466421b -r b0db5adf11c1 ja/examples/extdiff.extdiff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.extdiff.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg extdiff} + + +@@ -1 +1,2 @@ + The first line. ++The second line. diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,61 @@ +#!/bin/bash + +hg init a +cd a +mkdir -p examples src/watcher +touch COPYING MANIFEST.in README setup.py +touch examples/performant.py examples/simple.py +touch src/main.py src/watcher/_watcher.c src/watcher/watcher.py src/xyzzy.txt + +#$ name: files + +hg add COPYING README examples/simple.py + +#$ name: dirs + +hg status src + +#$ name: wdir-subdir + +cd src +hg add -n +hg add -n . + +#$ name: wdir-relname + +hg status +hg status `hg root` + +#$ name: glob.star + +hg add 'glob:*.py' + +#$ name: glob.starstar + +cd .. +hg status 'glob:**.py' + +#$ name: glob.star-starstar + +hg status 'glob:*.py' +hg status 'glob:**.py' + +#$ name: glob.question + +hg status 'glob:**.?' + +#$ name: glob.range + +hg status 'glob:**[nr-t]' + +#$ name: glob.group + +hg status 'glob:*.{in,py}' + +#$ name: filter.include + +hg status -I '*.in' + +#$ name: filter.exclude + +hg status -X '**.py' src diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.dirs.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.dirs.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status src} +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.files.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.files.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg add COPYING README examples/simple.py} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.filter.exclude.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.filter.exclude.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status -X '**.py' src} +? src/watcher/_watcher.c +? src/xyzzy.txt diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.filter.include.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.filter.include.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status -I '*.in'} +? MANIFEST.in diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.group.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.group.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status 'glob:*.\{in,py\}'} +? MANIFEST.in +? setup.py diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.question.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.question.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status 'glob:**.?'} +? src/watcher/_watcher.c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.range.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.range.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status 'glob:**[nr-t]'} +? MANIFEST.in +? src/xyzzy.txt diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.star-starstar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.star-starstar.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg status 'glob:*.py'} +? setup.py +$ \textbf{hg status 'glob:**.py'} +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.star.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.star.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg add 'glob:*.py'} +adding main.py diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.glob.starstar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.starstar.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cd ..} +$ \textbf{hg status 'glob:**.py'} +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.wdir-relname.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.wdir-relname.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ +$ \textbf{hg status} +A COPYING +A README +A examples/simple.py +? MANIFEST.in +? examples/performant.py +? setup.py +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt +$ \textbf{hg status `hg root`} +A ../COPYING +A ../README +A ../examples/simple.py +? ../MANIFEST.in +? ../examples/performant.py +? ../setup.py +? main.py +? watcher/_watcher.c +? watcher/watcher.py +? xyzzy.txt diff -r 7be02466421b -r b0db5adf11c1 ja/examples/filenames.wdir-subdir.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.wdir-subdir.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,14 @@ +$ \textbf{cd src} +$ \textbf{hg add -n} +adding ../MANIFEST.in +adding ../examples/performant.py +adding ../setup.py +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt +$ \textbf{hg add -n .} +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.msglen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,14 @@ +#!/bin/sh + +hg init a +cd a +echo '[hooks]' > .hg/hgrc +echo 'pretxncommit.msglen = test `hg tip --template {desc} | wc -c` -ge 10' >> .hg/hgrc + +#$ name: go + +cat .hg/hgrc +echo a > a +hg add a +hg commit -A -m 'too short' +hg commit -A -m 'long enough' diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.msglen.go.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen.go.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.msglen = test `hg tip --template \{desc\} | wc -c` -ge 10 +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -A -m 'too short'} +transaction abort! +rollback completed +abort: pretxncommit.msglen hook exited with status 1 +$ \textbf{hg commit -A -m 'long enough'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.msglen.run.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen.run.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.msglen = test `hg tip --template \{desc\} | wc -c` -ge 10 +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -A -m 'too short'} +abort: pretxncommit.msglen hook exited with status 1 +transaction abort! +rollback completed +$ \textbf{hg commit -A -m 'long enough'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,37 @@ +#!/bin/bash + +#$ name: init + +hg init hook-test +cd hook-test +echo '[hooks]' >> .hg/hgrc +echo 'commit = echo committed $HG_NODE' >> .hg/hgrc +cat .hg/hgrc +echo a > a +hg add a +hg commit -m 'testing commit hook' + +#$ name: ext +#$ ignore: ^date of commit.* + +echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc +echo a >> a +hg commit -m 'i have two hooks' + +#$ name: + +echo '#!/bin/sh' >> check_bug_id +echo '# check that a commit comment mentions a numeric bug id' >> check_bug_id +echo 'hg log -r $1 --template {desc} | grep -q "\> check_bug_id +chmod +x check_bug_id + +#$ name: pretxncommit + +cat check_bug_id + +echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc + +echo a >> a +hg commit -m 'i am not mentioning a bug id' + +hg commit -m 'i refer you to bug 666' diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.simple.ext.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.ext.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc} +$ \textbf{echo a >> a} +$ \textbf{hg commit -m 'i have two hooks'} +committed + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.simple.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg init hook-test} +$ \textbf{cd hook-test} +$ \textbf{echo '[hooks]' >> .hg/hgrc} +$ \textbf{echo 'commit = echo committed $HG_NODE' >> .hg/hgrc} +$ \textbf{cat .hg/hgrc} +[hooks] +commit = echo committed $HG_NODE +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -m 'testing commit hook'} +committed diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.simple.pretxncommit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.pretxncommit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{cat check_bug_id} +#!/bin/sh +# check that a commit comment mentions a numeric bug id +hg log -r $1 --template \{desc\} | grep -q "\textbackslash{}> .hg/hgrc} +$ \textbf{echo a >> a} +$ \textbf{hg commit -m 'i am not mentioning a bug id'} +transaction abort! +rollback completed +abort: pretxncommit.bug_id_required hook exited with status 1 +$ \textbf{hg commit -m 'i refer you to bug 666'} +committed + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,31 @@ +#!/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 7be02466421b -r b0db5adf11c1 ja/examples/hook.ws.better.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws.better.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,17 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.whitespace = .hg/check_whitespace.py +$ \textbf{echo 'a ' >> a} +$ \textbf{hg commit -A -m 'add new line with trailing whitespace'} +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ \textbf{sed -i 's, *$,,' a} +$ \textbf{hg commit -A -m 'trimmed trailing whitespace'} +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/hook.ws.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws.simple.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.whitespace = hg export tip | (! egrep -q '^\textbackslash{}+.*[ \textbackslash{}t]$') +$ \textbf{echo 'a ' > a} +$ \textbf{hg commit -A -m 'test with trailing whitespace'} +adding a +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ \textbf{echo 'a' > a} +$ \textbf{hg commit -A -m 'drop trailing whitespace and try again'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/issue29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/issue29 Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ +#!/bin/bash + +#$ name: go + +hg init issue29 +cd issue29 +echo a > a +hg ci -Ama +echo b > b +hg ci -Amb +hg up 0 +mkdir b +echo b > b/b +hg ci -Amc + +#$ ignore: abort: Is a directory: .* +hg merge + +#$ name: +# This error is expected from the failed merge. + +exit 0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/issue29.go.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/issue29.go.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg init issue29} +$ \textbf{cd issue29} +$ \textbf{echo a > a} +$ \textbf{hg ci -Ama} +adding a +$ \textbf{echo b > b} +$ \textbf{hg ci -Amb} +adding b +$ \textbf{hg up 0} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{hg ci -Amc} +adding b/b +$ \textbf{hg merge} + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.dodiff Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,14 @@ +#!/bin/bash + +#$ name: diff + +echo 'this is my original thought' > oldfile +echo 'i have changed my mind' > newfile + +diff -u oldfile newfile > tiny.patch + +cat tiny.patch + +patch < tiny.patch + +cat oldfile diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.dodiff.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.dodiff.diff.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{echo 'this is my original thought' > oldfile} +$ \textbf{echo 'i have changed my mind' > newfile} +$ \textbf{diff -u oldfile newfile > tiny.patch} +$ \textbf{cat tiny.patch} + + +@@ -1 +1 @@ +-this is my original thought ++i have changed my mind +$ \textbf{patch < tiny.patch} +patching file oldfile +$ \textbf{cat oldfile} +i have changed my mind diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,67 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +hg init a +cd a + +#$ name: init + +hg qinit +hg qnew hello.patch +echo hello > hello +hg add hello +hg qrefresh +hg qnew goodbye.patch +echo goodbye > goodbye +hg add goodbye +hg qrefresh + +#$ name: qguard + +hg qguard + +#$ name: qguard.pos + +hg qguard +foo +hg qguard + +#$ name: qguard.neg + +hg qguard hello.patch -quux +hg qguard hello.patch + +#$ name: series + +cat .hg/patches/series + +#$ name: qselect.foo + +hg qpop -a +hg qselect +hg qselect foo +hg qselect + +#$ name: qselect.cat + +cat .hg/patches/guards + +#$ name: qselect.qpush +hg qpush -a + +#$ name: qselect.error + +hg qselect +foo + +#$ name: qselect.quux + +hg qselect quux +hg qpop -a +hg qpush -a + +#$ name: qselect.foobar + +hg qselect foo bar +hg qpop -a +hg qpush -a diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg qinit} +$ \textbf{hg qnew hello.patch} +$ \textbf{echo hello > hello} +$ \textbf{hg add hello} +$ \textbf{hg qrefresh} +$ \textbf{hg qnew goodbye.patch} +$ \textbf{echo goodbye > goodbye} +$ \textbf{hg add goodbye} +$ \textbf{hg qrefresh} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qguard.neg.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.neg.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg qguard hello.patch -quux} +$ \textbf{hg qguard hello.patch} +hello.patch: -quux diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qguard.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg qguard} +goodbye.patch: unguarded diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qguard.pos.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.pos.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg qguard +foo} +$ \textbf{hg qguard} +goodbye.patch: +foo diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.cat.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat .hg/patches/guards} +foo diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.error.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.error.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg qselect +foo} +abort: guard '+foo' starts with invalid character: '+' diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.foo.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.foo.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{hg qselect} +no active guards +$ \textbf{hg qselect foo} +number of unguarded, unapplied patches has changed from 1 to 2 +$ \textbf{hg qselect} +foo diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.foobar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.foobar.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qselect foo bar} +number of unguarded, unapplied patches has changed from 0 to 2 +$ \textbf{hg qpop -a} +no patches applied +$ \textbf{hg qpush -a} +applying hello.patch +applying goodbye.patch +Now at: goodbye.patch diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.qpush.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.qpush.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg qpush -a} +applying hello.patch +applying goodbye.patch +Now at: goodbye.patch diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.qselect.quux.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.quux.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg qselect quux} +number of guarded, applied patches has changed from 0 to 2 +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{hg qpush -a} +patch series already fully applied diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.guards.series.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.series.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat .hg/patches/series} +hello.patch #-quux +goodbye.patch #+foo diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.id --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +#!/bin/sh + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +hg init a +cd a +hg qinit +echo 'int x;' > test.c +hg ci -Ama + +hg qnew first.patch +echo 'float c;' >> test.c +hg qrefresh + +hg qnew second.patch +echo 'double u;' > other.c +hg add other.c +hg qrefresh + +#$ name: output + +hg qapplied +hg log -r qbase:qtip +hg export second.patch + +#$ name: +exit 0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.id.out.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id.out.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,32 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg log -r qbase:qtip} +changeset: +tag: first.patch +tag: qbase +user: Bryan O'Sullivan + +summary: patch queue: first.patch + +changeset: +tag: second.patch +tag: qtip +tag: tip +user: Bryan O'Sullivan + +summary: patch queue: second.patch + +$ \textbf{hg export second.patch} +# HG changeset patch +# User Bryan O'Sullivan + +# Node ID +# Parent +patch queue: second.patch + +diff -r -r other.c + + +@@ -0,0 +1,1 @@ ++double u; diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.id.output.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id.output.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,32 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg log -r qbase:qtip} +changeset: +tag: first.patch +tag: qbase +user: Bryan O'Sullivan + +summary: [mq]: first.patch + +changeset: +tag: qtip +tag: second.patch +tag: tip +user: Bryan O'Sullivan + +summary: [mq]: second.patch + +$ \textbf{hg export second.patch} +# HG changeset patch +# User Bryan O'Sullivan + +# Node ID +# Parent +[mq]: second.patch + +diff -r -r other.c + + +@@ -0,0 +1,1 @@ ++double u; diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.qinit-help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.qinit-help Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: help +hg help qinit diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.qinit-help.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.qinit-help.help.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg help qinit} +hg qinit [-c] + +init a new queue repository + + The queue repository is unversioned by default. If -c is + specified, qinit will create a separate nested repository + for patches (qinit -c may also be run later to convert + an unversioned patch repository into a versioned one). + You can use qcommit to commit changes to this queue repository. + +options: + + -c --create-repo create queue repository + +use "hg -v help qinit" to show global options diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,51 @@ +#!/bin/bash + +cp $EXAMPLE_DIR/data/netplug-*.tar.bz2 . +ln -s /bin/true download +export PATH=`pwd`:$PATH + +#$ name: download + +download netplug-1.2.5.tar.bz2 +tar jxf netplug-1.2.5.tar.bz2 +cd netplug-1.2.5 +hg init +hg commit -q --addremove --message netplug-1.2.5 +cd .. +hg clone netplug-1.2.5 netplug + +#$ name: + +cd netplug +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC +cd .. + +#$ name: qinit + +cd netplug +hg qinit +hg qnew -m 'fix build problem with gcc 4' build-fix.patch +perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c +hg qrefresh +hg tip -p + +#$ name: newsource + +hg qpop -a +cd .. +download netplug-1.2.8.tar.bz2 +hg clone netplug-1.2.5 netplug-1.2.8 +cd netplug-1.2.8 +hg locate -0 | xargs -0 rm +cd .. +tar jxf netplug-1.2.8.tar.bz2 +cd netplug-1.2.8 +hg commit --addremove --message netplug-1.2.8 + +#$ name: repush + +cd ../netplug +hg pull ../netplug-1.2.8 +hg qpush -a + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tarball.download.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.download.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{download netplug-1.2.5.tar.bz2} +$ \textbf{tar jxf netplug-1.2.5.tar.bz2} +$ \textbf{cd netplug-1.2.5} +$ \textbf{hg init} +$ \textbf{hg commit -q --addremove --message netplug-1.2.5} +$ \textbf{cd ..} +$ \textbf{hg clone netplug-1.2.5 netplug} +18 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tarball.newsource.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.newsource.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{cd ..} +$ \textbf{download netplug-1.2.8.tar.bz2} +$ \textbf{hg clone netplug-1.2.5 netplug-1.2.8} +18 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd netplug-1.2.8} +$ \textbf{hg locate -0 | xargs -0 rm} +$ \textbf{cd ..} +$ \textbf{tar jxf netplug-1.2.8.tar.bz2} +$ \textbf{cd netplug-1.2.8} +$ \textbf{hg commit --addremove --message netplug-1.2.8} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tarball.qinit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.qinit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +$ \textbf{cd netplug} +$ \textbf{hg qinit} +$ \textbf{hg qnew -m 'fix build problem with gcc 4' build-fix.patch} +$ \textbf{perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c} +$ \textbf{hg qrefresh} +$ \textbf{hg tip -p} +changeset: +tag: qtip +tag: build-fix.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan + +summary: fix build problem with gcc 4 + +diff -r -r netlink.c + + +@@ -275,7 +275,7 @@ netlink_open(void) + exit(1); + \} + +- int addr_len = sizeof(addr); ++ socklen_t addr_len = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) \{ + do_log(LOG_ERR, "Could not get socket details: %m"); + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tarball.repush.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.repush.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{cd ../netplug} +$ \textbf{hg pull ../netplug-1.2.8} +pulling from ../netplug-1.2.8 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 12 changes to 12 files +(run 'hg update' to get a working copy) +$ \textbf{hg qpush -a} +applying build-fix.patch +Now at: build-fix.patch diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tools --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +#!/bin/bash + +cp $EXAMPLE_DIR/data/remove-redundant-null-checks.patch . + +#$ name: tools +diffstat -p1 remove-redundant-null-checks.patch + +filterdiff -i '*/video/*' remove-redundant-null-checks.patch + +#$ name: lsdiff +lsdiff -nvv remove-redundant-null-checks.patch diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tools.lsdiff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools.lsdiff.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,21 @@ +$ \textbf{lsdiff -nvv remove-redundant-null-checks.patch} +22 File #1 a/drivers/char/agp/sgi-agp.c + 24 Hunk #1 static int __devinit agp_sgi_init(void) +37 File #2 a/drivers/char/hvcs.c + 39 Hunk #1 static struct tty_operations hvcs_ops = + 53 Hunk #2 static int hvcs_alloc_index_list(int n) +69 File #3 a/drivers/message/fusion/mptfc.c + 71 Hunk #1 mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in +85 File #4 a/drivers/message/fusion/mptsas.c + 87 Hunk #1 mptsas_probe_hba_phys(MPT_ADAPTER *ioc) +98 File #5 a/drivers/net/fs_enet/fs_enet-mii.c + 100 Hunk #1 static struct fs_enet_mii_bus *create_bu +111 File #6 a/drivers/net/wireless/ipw2200.c + 113 Hunk #1 static struct ipw_fw_error *ipw_alloc_er + 126 Hunk #2 static ssize_t clear_error(struct device + 140 Hunk #3 static void ipw_irq_tasklet(struct ipw_p + 150 Hunk #4 static void ipw_pci_remove(struct pci_de +164 File #7 a/drivers/scsi/libata-scsi.c + 166 Hunk #1 int ata_cmd_ioctl(struct scsi_device *sc +178 File #8 a/drivers/video/au1100fb.c + 180 Hunk #1 void __exit au1100fb_cleanup(void) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tools.tools.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools.tools.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,23 @@ +$ \textbf{diffstat -p1 remove-redundant-null-checks.patch} + drivers/char/agp/sgi-agp.c | 5 ++--- + drivers/char/hvcs.c | 11 +++++------ + drivers/message/fusion/mptfc.c | 6 ++---- + drivers/message/fusion/mptsas.c | 3 +-- + drivers/net/fs_enet/fs_enet-mii.c | 3 +-- + drivers/net/wireless/ipw2200.c | 22 ++++++---------------- + drivers/scsi/libata-scsi.c | 4 +--- + drivers/video/au1100fb.c | 3 +-- + 8 files changed, 19 insertions(+), 38 deletions(-) +$ \textbf{filterdiff -i '*/video/*' remove-redundant-null-checks.patch} + + +@@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) + \{ + driver_unregister(&au1100fb_driver); + +- if (drv_info.opt_mode) +- kfree(drv_info.opt_mode); ++ kfree(drv_info.opt_mode); + \} + + module_init(au1100fb_init); diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: qinit + +hg init mq-sandbox +cd mq-sandbox +echo 'line 1' > file1 +echo 'another line 1' > file2 +hg add file1 file2 +hg commit -m'first change' + +hg qinit + +#$ name: qnew + +hg tip +hg qnew first.patch +hg tip +ls .hg/patches + +#$ name: qrefresh +#$ ignore: \s+200[78]-.* + +echo 'line 2' >> file1 +hg diff +hg qrefresh +hg diff +hg tip --style=compact --patch + +#$ name: qrefresh2 + +echo 'line 3' >> file1 +hg status +hg qrefresh +hg tip --style=compact --patch + +#$ name: qnew2 + +hg qnew second.patch +hg log --style=compact --limit=2 +echo 'line 4' >> file1 +hg qrefresh +hg tip --style=compact --patch +hg annotate file1 + +#$ name: qseries + +hg qseries +hg qapplied + +#$ name: qpop + +hg qapplied +hg qpop +hg qseries +hg qapplied +cat file1 + +#$ name: qpush-a + +hg qpush -a +cat file1 + +#$ name: add + +echo 'file 3, line 1' >> file3 +hg qnew add-file3.patch +hg qnew -f add-file3.patch + +#$ name: +exit 0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.add.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{echo 'file 3, line 1' >> file3} +$ \textbf{hg qnew add-file3.patch} +$ \textbf{hg qnew -f add-file3.patch} +abort: patch "add-file3.patch" already exists diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qinit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qinit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init mq-sandbox} +$ \textbf{cd mq-sandbox} +$ \textbf{echo 'line 1' > file1} +$ \textbf{echo 'another line 1' > file2} +$ \textbf{hg add file1 file2} +$ \textbf{hg commit -m'first change'} +$ \textbf{hg qinit} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qnew.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qnew.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,20 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: first change + +$ \textbf{hg qnew first.patch} +$ \textbf{hg tip} +changeset: +tag: qtip +tag: first.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan + +summary: [mq]: first.patch + +$ \textbf{ls .hg/patches} +first.patch series status diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qnew2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qnew2.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +$ \textbf{hg qnew second.patch} +$ \textbf{hg log --style=compact --limit=2} +2[qtip,second.patch,tip] + [mq]: second.patch + +1[first.patch,qbase] + [mq]: first.patch + +$ \textbf{echo 'line 4' >> file1} +$ \textbf{hg qrefresh} +$ \textbf{hg tip --style=compact --patch} +2[qtip,second.patch,tip] + [mq]: second.patch + +diff -r -r file1 + + +@@ -1,3 +1,4 @@ line 1 + line 1 + line 2 + line 3 ++line 4 + +$ \textbf{hg annotate file1} +0: line 1 +1: line 2 +1: line 3 +2: line 4 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qpop.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qpop.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,14 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg qpop} +Now at: first.patch +$ \textbf{hg qseries} +first.patch +second.patch +$ \textbf{hg qapplied} +first.patch +$ \textbf{cat file1} +line 1 +line 2 +line 3 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qpush-a.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qpush-a.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qpush -a} +applying second.patch +Now at: second.patch +$ \textbf{cat file1} +line 1 +line 2 +line 3 +line 4 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qrefresh.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qrefresh.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,21 @@ +$ \textbf{echo 'line 2' >> file1} +$ \textbf{hg diff} +diff -r file1 + + +@@ -1,1 +1,2 @@ line 1 + line 1 ++line 2 +$ \textbf{hg qrefresh} +$ \textbf{hg diff} +$ \textbf{hg tip --style=compact --patch} +1[qtip,first.patch,tip,qbase] + [mq]: first.patch + +diff -r -r file1 + + +@@ -1,1 +1,2 @@ line 1 + line 1 ++line 2 + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qrefresh2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qrefresh2.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,16 @@ +$ \textbf{echo 'line 3' >> file1} +$ \textbf{hg status} +M file1 +$ \textbf{hg qrefresh} +$ \textbf{hg tip --style=compact --patch} +1[qtip,first.patch,tip,qbase] + [mq]: first.patch + +diff -r -r file1 + + +@@ -1,1 +1,3 @@ line 1 + line 1 ++line 2 ++line 3 + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/mq.tutorial.qseries.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qseries.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg qseries} +first.patch +second.patch +$ \textbf{hg qapplied} +first.patch +second.patch diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rename.divergent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,33 @@ +#!/bin/bash + +hg init orig +cd orig +echo foo > foo +hg ci -A -m 'First commit' +cd .. + +#$ name: clone + +hg clone orig anne +hg clone orig bob + +#$ name: rename.anne + +cd anne +hg mv foo bar +hg ci -m 'Rename foo to bar' + +#$ name: rename.bob + +cd ../bob +hg mv foo quux +hg ci -m 'Rename foo to quux' + +#$ name: merge +# See http://www.selenic.com/mercurial/bts/issue455 + +cd ../orig +hg pull -u ../anne +hg pull ../bob +hg merge +ls diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rename.divergent.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg clone orig anne} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg clone orig bob} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rename.divergent.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,26 @@ +# See http://www.selenic.com/mercurial/bts/issue455 +$ \textbf{cd ../orig} +$ \textbf{hg pull -u ../anne} +pulling from ../anne +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg pull ../bob} +pulling from ../bob +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +warning: detected divergent renames of foo to: + bar + quux +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{ls} +bar quux diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rename.divergent.rename.anne.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.rename.anne.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd anne} +$ \textbf{hg mv foo bar} +$ \textbf{hg ci -m 'Rename foo to bar'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rename.divergent.rename.bob.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.rename.bob.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ../bob} +$ \textbf{hg mv foo quux} +$ \textbf{hg ci -m 'Rename foo to quux'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,37 @@ +#!/bin/bash + +hg init a +cd a +echo a > a +hg ci -A -m 'First commit' + +echo a >> a + +#$ name: tip + +#$ name: commit + +hg status +echo b > b +hg commit -m 'Add file b' + +#$ name: status + +hg status +hg tip + +#$ name: rollback + +hg rollback +hg tip +hg status + +#$ name: add + +hg add b +hg commit -m 'Add file b, this time for real' + +#$ name: twice + +hg rollback +hg rollback diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.add.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg add b} +$ \textbf{hg commit -m 'Add file b, this time for real'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.commit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status} +M a +$ \textbf{echo b > b} +$ \textbf{hg commit -m 'Add file b'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.rollback.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.rollback.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg rollback} +rolling back last transaction +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: First commit + +$ \textbf{hg status} +M a +? b diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg status} +? b +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Add file b + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.tip.out diff -r 7be02466421b -r b0db5adf11c1 ja/examples/rollback.twice.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.twice.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg rollback} +rolling back last transaction +$ \textbf{hg rollback} +no rollback information available diff -r 7be02466421b -r b0db5adf11c1 ja/examples/run-example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/run-example Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# +# This program takes something that resembles a shell script and runs +# it, spitting input (commands from the script) and output into text +# files, for use in examples. + +import cStringIO +import errno +import getopt +import os +import pty +import re +import select +import shutil +import signal +import stat +import sys +import tempfile +import time + +tex_subs = { + '\\': '\\textbackslash{}', + '{': '\\{', + '}': '\\}', + } + +def gensubs(s): + start = 0 + for i, c in enumerate(s): + sub = tex_subs.get(c) + if sub: + yield s[start:i] + start = i + 1 + yield sub + yield s[start:] + +def tex_escape(s): + return ''.join(gensubs(s)) + +def maybe_unlink(name): + try: + os.unlink(name) + return True + except OSError, err: + if err.errno != errno.ENOENT: + raise + return False + +def find_path_to(program): + for p in os.environ.get('PATH', os.defpath).split(os.pathsep): + name = os.path.join(p, program) + if os.access(name, os.X_OK): + return p + return None + +class example: + shell = '/usr/bin/env bash' + ps1 = '__run_example_ps1__ ' + ps2 = '__run_example_ps2__ ' + pi_re = re.compile(r'#\$\s*(name|ignore):\s*(.*)$') + + timeout = 10 + + def __init__(self, name, verbose): + self.name = name + self.verbose = verbose + self.poll = select.poll() + + def parse(self): + '''yield each hunk of input from the file.''' + fp = open(self.name) + cfp = cStringIO.StringIO() + for line in fp: + cfp.write(line) + if not line.rstrip().endswith('\\'): + yield cfp.getvalue() + cfp.seek(0) + cfp.truncate() + + def status(self, s): + sys.stdout.write(s) + if not s.endswith('\n'): + sys.stdout.flush() + + def send(self, s): + if self.verbose: + print >> sys.stderr, '>', self.debugrepr(s) + while s: + count = os.write(self.cfd, s) + s = s[count:] + + def debugrepr(self, s): + rs = repr(s) + limit = 60 + if len(rs) > limit: + return ('%s%s ... [%d bytes]' % (rs[:limit], rs[0], len(s))) + else: + return rs + + timeout = 5 + + def read(self, hint): + events = self.poll.poll(self.timeout * 1000) + if not events: + print >> sys.stderr, ('[%stimed out after %d seconds]' % + (hint, self.timeout)) + os.kill(self.pid, signal.SIGHUP) + return '' + return os.read(self.cfd, 1024) + + def receive(self, hint): + out = cStringIO.StringIO() + while True: + try: + if self.verbose: + sys.stderr.write('< ') + s = self.read(hint) + except OSError, err: + if err.errno == errno.EIO: + return '', '' + raise + if self.verbose: + print >> sys.stderr, self.debugrepr(s) + out.write(s) + s = out.getvalue() + if s.endswith(self.ps1): + return self.ps1, s.replace('\r\n', '\n')[:-len(self.ps1)] + if s.endswith(self.ps2): + return self.ps2, s.replace('\r\n', '\n')[:-len(self.ps2)] + + def sendreceive(self, s, hint): + self.send(s) + ps, r = self.receive(hint) + if r.startswith(s): + r = r[len(s):] + return ps, r + + def run(self): + ofp = None + basename = os.path.basename(self.name) + self.status('running %s ' % basename) + tmpdir = tempfile.mkdtemp(prefix=basename) + + # remove the marker file that we tell make to use to see if + # this run succeeded + maybe_unlink(self.name + '.run') + + rcfile = os.path.join(tmpdir, '.hgrc') + rcfp = open(rcfile, 'w') + print >> rcfp, '[ui]' + print >> rcfp, "username = Bryan O'Sullivan " + + rcfile = os.path.join(tmpdir, '.bashrc') + rcfp = open(rcfile, 'w') + print >> rcfp, 'PS1="%s"' % self.ps1 + print >> rcfp, 'PS2="%s"' % self.ps2 + print >> rcfp, 'unset HISTFILE' + path = ['/usr/bin', '/bin'] + hg = find_path_to('hg') + if hg and hg not in path: + path.append(hg) + def re_export(envar): + v = os.getenv(envar) + if v is not None: + print >> rcfp, 'export ' + envar + '=' + v + print >> rcfp, 'export PATH=' + ':'.join(path) + re_export('PYTHONPATH') + print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd() + print >> rcfp, 'export HGMERGE=merge' + print >> rcfp, 'export LANG=C' + print >> rcfp, 'export LC_ALL=C' + print >> rcfp, 'export TZ=GMT' + print >> rcfp, 'export HGRC="%s/.hgrc"' % tmpdir + print >> rcfp, 'export HGRCPATH=$HGRC' + print >> rcfp, 'cd %s' % tmpdir + rcfp.close() + sys.stdout.flush() + sys.stderr.flush() + self.pid, self.cfd = pty.fork() + if self.pid == 0: + cmdline = ['/usr/bin/env', '-i', 'bash', '--noediting', + '--noprofile', '--norc'] + try: + os.execv(cmdline[0], cmdline) + except OSError, err: + print >> sys.stderr, '%s: %s' % (cmdline[0], err.strerror) + sys.stderr.flush() + os._exit(0) + self.poll.register(self.cfd, select.POLLIN | select.POLLERR | + select.POLLHUP) + + prompts = { + '': '', + self.ps1: '$', + self.ps2: '>', + } + + ignore = [ + r'\d+:[0-9a-f]{12}', # changeset number:hash + r'[0-9a-f]{40}', # long changeset hash + r'[0-9a-f]{12}', # short changeset hash + r'^(?:---|\+\+\+) .*', # diff header with dates + r'^date:.*', # date + #r'^diff -r.*', # "diff -r" is followed by hash + r'^# Date \d+ \d+', # hg patch header + ] + + err = False + read_hint = '' + + try: + try: + # eat first prompt string from shell + self.read(read_hint) + # setup env and prompt + ps, output = self.sendreceive('source %s\n' % rcfile, + read_hint) + for hunk in self.parse(): + # is this line a processing instruction? + m = self.pi_re.match(hunk) + if m: + pi, rest = m.groups() + if pi == 'name': + self.status('.') + out = rest + if out in ('err', 'lxo', 'out', 'run', 'tmp'): + print >> sys.stderr, ('%s: illegal section ' + 'name %r' % + (self.name, out)) + return 1 + assert os.sep not in out + if ofp is not None: + ofp.close() + err |= self.rename_output(ofp_basename, ignore) + if out: + ofp_basename = '%s.%s' % (self.name, out) + read_hint = ofp_basename + ' ' + ofp = open(ofp_basename + '.tmp', 'w') + else: + ofp = None + elif pi == 'ignore': + ignore.append(rest) + elif hunk.strip(): + # it's something we should execute + newps, output = self.sendreceive(hunk, read_hint) + if not ofp: + continue + # first, print the command we ran + if not hunk.startswith('#'): + nl = hunk.endswith('\n') + hunk = ('%s \\textbf{%s}' % + (prompts[ps], + tex_escape(hunk.rstrip('\n')))) + if nl: hunk += '\n' + ofp.write(hunk) + # then its output + ofp.write(tex_escape(output)) + ps = newps + self.status('\n') + except: + print >> sys.stderr, '(killed)' + os.kill(self.pid, signal.SIGKILL) + pid, rc = os.wait() + raise + else: + try: + ps, output = self.sendreceive('exit\n', read_hint) + if ofp is not None: + ofp.write(output) + ofp.close() + err |= self.rename_output(ofp_basename, ignore) + os.close(self.cfd) + except IOError: + pass + os.kill(self.pid, signal.SIGTERM) + pid, rc = os.wait() + err = err or rc + if err: + if os.WIFEXITED(rc): + print >> sys.stderr, '(exit %s)' % os.WEXITSTATUS(rc) + elif os.WIFSIGNALED(rc): + print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc) + else: + open(self.name + '.run', 'w') + return err + finally: + shutil.rmtree(tmpdir) + + def rename_output(self, base, ignore): + mangle_re = re.compile('(?:' + '|'.join(ignore) + ')') + def mangle(s): + return mangle_re.sub('', s) + def matchfp(fp1, fp2): + while True: + s1 = mangle(fp1.readline()) + s2 = mangle(fp2.readline()) + if cmp(s1, s2): + break + if not s1: + return True + return False + + oldname = base + '.out' + tmpname = base + '.tmp' + errname = base + '.err' + errfp = open(errname, 'w+') + for line in open(tmpname): + errfp.write(mangle_re.sub('', line)) + os.rename(tmpname, base + '.lxo') + errfp.seek(0) + try: + oldfp = open(oldname) + except IOError, err: + if err.errno != errno.ENOENT: + raise + os.rename(errname, oldname) + return False + if matchfp(oldfp, errfp): + os.unlink(errname) + return False + else: + print >> sys.stderr, '\nOutput of %s has changed!' % base + os.system('diff -u %s %s 1>&2' % (oldname, errname)) + return True + +def print_help(exit, msg=None): + if msg: + print >> sys.stderr, 'Error:', msg + print >> sys.stderr, 'Usage: run-example [options] [test...]' + print >> sys.stderr, 'Options:' + print >> sys.stderr, ' -a --all run all tests in this directory' + print >> sys.stderr, ' -h --help print this help message' + print >> sys.stderr, ' -v --verbose display extra debug output' + sys.exit(exit) + +def main(path='.'): + opts, args = getopt.getopt(sys.argv[1:], '?ahv', + ['all', 'help', 'verbose']) + verbose = False + run_all = False + for o, a in opts: + if o in ('-h', '-?', '--help'): + print_help(0) + if o in ('-a', '--all'): + run_all = True + if o in ('-v', '--verbose'): + verbose = True + errs = 0 + if args: + for a in args: + try: + st = os.lstat(a) + except OSError, err: + print >> sys.stderr, '%s: %s' % (a, err.strerror) + errs += 1 + continue + if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: + if example(a, verbose).run(): + errs += 1 + else: + print >> sys.stderr, '%s: not a file, or not executable' % a + errs += 1 + elif run_all: + names = os.listdir(path) + names.sort() + for name in names: + if name == 'run-example' or name.startswith('.'): continue + if name.endswith('.out') or name.endswith('~'): continue + if name.endswith('.run'): continue + pathname = os.path.join(path, name) + try: + st = os.lstat(pathname) + except OSError, err: + # could be an output file that was removed while we ran + if err.errno != errno.ENOENT: + raise + continue + if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: + if example(pathname, verbose).run(): + errs += 1 + print >> open(os.path.join(path, '.run'), 'w'), time.asctime() + else: + print_help(1, msg='no test names given, and --all not provided') + return errs + +if __name__ == '__main__': + try: + sys.exit(main()) + except KeyboardInterrupt: + print >> sys.stderr, 'interrupted!' + sys.exit(1) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/svn-long.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn-long.txt Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines +Changed paths: + M /gen2/trunk/src/linux-kernel/infiniband/core/cma.c + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/svn-short.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn-short.txt Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/svn.style --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn.style Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template diff -r 7be02466421b -r b0db5adf11c1 ja/examples/svn.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn.template Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,44 @@ +#!/bin/bash + +#$ name: init + +hg init mytag +cd mytag + +echo hello > myfile +hg commit -A -m 'Initial commit' + +#$ name: tag + +hg tag v1.0 + +#$ name: tags + +hg tags + +#$ name: log + +hg log + +#$ name: log.v1.0 + +echo goodbye > myfile2 +hg commit -A -m 'Second commit' +hg log -r v1.0 + +#$ name: remove + +hg tag --remove v1.0 +hg tags + +#$ name: replace + +hg tag -r 1 v1.1 +hg tags +hg tag -r 2 v1.1 +hg tag -f -r 2 v1.1 +hg tags + +#$ name: tip + +hg tip diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.init.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init mytag} +$ \textbf{cd mytag} +$ \textbf{echo hello > myfile} +$ \textbf{hg commit -A -m 'Initial commit'} +adding myfile diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.log.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg log} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.0 for changeset + +changeset: +tag: v1.0 +user: Bryan O'Sullivan + +summary: Initial commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.log.v1.0.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.log.v1.0.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{echo goodbye > myfile2} +$ \textbf{hg commit -A -m 'Second commit'} +adding myfile2 +$ \textbf{hg log -r v1.0} +changeset: +tag: v1.0 +user: Bryan O'Sullivan + +summary: Initial commit + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.remove.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg tag --remove v1.0} +$ \textbf{hg tags} +tip diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.replace.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.replace.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg tag -r 1 v1.1} +$ \textbf{hg tags} +tip +v1.1 +$ \textbf{hg tag -r 2 v1.1} +abort: a tag named v1.1 already exists (use -f to force) +$ \textbf{hg tag -f -r 2 v1.1} +$ \textbf{hg tags} +tip +v1.1 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tag.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg tag v1.0} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.tags.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tags.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg tags} +tip +v1.0 diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tag.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tip.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.1 for changeset + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,96 @@ +#!/bin/bash + +# So many different bits of random output, it would be a nightmare to +# ignore each individually. +#$ ignore: .* + +hg init myrepo +cd myrepo +echo hello > hello +hg commit -Am'added hello' + +echo hello >> hello +echo goodbye > goodbye +echo ' added line to end of <> file.' > ../msg +echo '' >> ../msg +echo 'in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.' >> ../msg + +hg commit -Al../msg + +hg tag mytag +hg tag v0.1 + +#$ name: normal + +hg log -r1 + +#$ name: compact + +hg log --style compact + +#$ name: changelog + +hg log --style changelog + +#$ name: simplest + +hg log -r1 --template 'i saw a changeset\n' + +#$ name: simplesub + +hg log --template 'i saw a changeset: {desc}\n' + +#$ name: keywords + +hg log -r1 --template 'author: {author}\n' +hg log -r1 --template 'desc:\n{desc}\n' +hg log -r1 --template 'files: {files}\n' +hg log -r1 --template 'file_adds: {file_adds}\n' +hg log -r1 --template 'file_dels: {file_dels}\n' +hg log -r1 --template 'node: {node}\n' +hg log -r1 --template 'parents: {parents}\n' +hg log -r1 --template 'rev: {rev}\n' +hg log -r1 --template 'tags: {tags}\n' + +#$ name: datekeyword + +hg log -r1 --template 'date: {date}\n' +hg log -r1 --template 'date: {date|isodate}\n' + +#$ name: manyfilters + +hg log -r1 --template '{author}\n' +hg log -r1 --template '{author|domain}\n' +hg log -r1 --template '{author|email}\n' +hg log -r1 --template '{author|obfuscate}\n' | cut -c-76 +hg log -r1 --template '{author|person}\n' +hg log -r1 --template '{author|user}\n' + +hg log -r1 --template 'looks almost right, but actually garbage: {date}\n' +hg log -r1 --template '{date|age}\n' +hg log -r1 --template '{date|date}\n' +hg log -r1 --template '{date|hgdate}\n' +hg log -r1 --template '{date|isodate}\n' +hg log -r1 --template '{date|rfc822date}\n' +hg log -r1 --template '{date|shortdate}\n' + +hg log -r1 --template '{desc}\n' | cut -c-76 +hg log -r1 --template '{desc|addbreaks}\n' | cut -c-76 +hg log -r1 --template '{desc|escape}\n' | cut -c-76 +hg log -r1 --template '{desc|fill68}\n' +hg log -r1 --template '{desc|fill76}\n' +hg log -r1 --template '{desc|firstline}\n' +hg log -r1 --template '{desc|strip}\n' | cut -c-76 +hg log -r1 --template '{desc|tabindent}\n' | expand | cut -c-76 + +hg log -r1 --template '{node}\n' +hg log -r1 --template '{node|short}\n' + +#$ name: combine + +hg log -r1 --template 'description:\n\t{desc|strip|fill68|tabindent}\n' + +#$ name: rev + +echo 'changeset = "rev: {rev}\n"' > rev +hg log -l1 --style ./rev diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.changelog.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.changelog.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.combine.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.combine.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.compact.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.compact.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.datekeyword.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.datekeyword.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.keywords.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.keywords.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.manyfilters.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.manyfilters.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.normal.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.normal.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.rev.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.rev.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.simplest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.simplest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.simple.simplesub.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.simplesub.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ + + + + + + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,70 @@ +#!/bin/bash + +svn() { + cat $EXAMPLE_DIR/svn-short.txt +} + +#$ name: short + +svn log -r9653 + +#$ name: + +hg init myrepo +cd myrepo + +echo hello > hello +hg commit -Am'added hello' + +echo hello >> hello +echo goodbye > goodbye +echo ' added line to end of <> file.' > ../msg +echo '' >> ../msg +echo 'in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.' >> ../msg + +hg commit -Al../msg + +hg tag mytag +hg tag v0.1 + +echo 'changeset = "{node|short}\n"' > svn.style + +#$ name: id + +hg log -r0 --template '{node}' + +#$ name: simplest + +cat svn.style +hg log -r1 --style svn.style + +#$ name: + +echo 'changeset =' > broken.style + +#$ name: syntax.input + +cat broken.style + +#$ name: syntax.error + +hg log -r1 --style broken.style + +#$ name: + +cp $EXAMPLE_DIR/svn.style . +cp $EXAMPLE_DIR/svn.template . + +#$ name: template + +cat svn.template + +#$ name: style + +cat svn.style + +#$ name: result +#$ ignore: \| 200[78].* + +hg log -r1 --style svn.style + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.id.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.id.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg log -r0 --template '\{node\}'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.result.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.result.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg log -r1 --style svn.style} +------------------------------------------------------------------------ + +r1 | bos + +added line to end of <> file. + +in addition, added a file with the helpful name (at least i hope that some +might consider it so) of goodbye. + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.short.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.short.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{svn log -r9653} +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.simplest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.simplest.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{cat svn.style} +changeset = "\{node|short\}\textbackslash{}n" +$ \textbf{hg log -r1 --style svn.style} + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.style.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.style.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat svn.style} +header = '------------------------------------------------------------------------\textbackslash{}n\textbackslash{}n' +changeset = svn.template diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.syntax.error.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.syntax.error.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg log -r1 --style broken.style} +abort: broken.style:1: parse error diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.syntax.input.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.syntax.input.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat broken.style} +changeset = diff -r 7be02466421b -r b0db5adf11c1 ja/examples/template.svnstyle.template.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.template.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cat svn.template} +r\{rev\} | \{author|user\} | \{date|isodate\} (\{date|rfc822date\}) + +\{desc|strip|fill76\} + +------------------------------------------------------------------------ diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,178 @@ +#!/bin/bash + +#$ name: version + +hg version + +#$ name: help + +hg help init + +#$ name: clone + +hg clone http://hg.serpentine.com/tutorial/hello + +#$ name: ls +#$ ignore: ^drwx.* +#$ ignore: ^total \d+ + +ls -l +ls hello + +#$ name: ls-a + +cd hello +ls -a + +#$ name: log + +hg log + +#$ name: log-r + +hg log -r 3 +hg log -r ff5d7b70a2a9 +hg log -r 1 -r 4 + +#$ name: log.range + +hg log -r 2:4 + +#$ name: log-v + +hg log -v -r 3 + +#$ name: log-vp + +hg log -v -p -r 2 + +#$ name: reclone + +cd .. +hg clone hello my-hello +cd my-hello + +#$ name: sed + +sed -i '/printf/a\\tprintf("hello again!\\n");' hello.c + +#$ name: status + +ls +hg status + +#$ name: diff + +hg diff + +#$ name: + +export HGEDITOR='echo Added an extra line of output >' + +#$ name: commit + +hg commit + +#$ name: tip + +hg tip -vp + +#$ name: clone-pull + +cd .. +hg clone hello hello-pull + +#$ name: incoming + +cd hello-pull +hg incoming ../my-hello + +#$ name: pull + +hg tip +hg pull ../my-hello +hg tip + +#$ name: update + +grep printf hello.c +hg update tip +grep printf hello.c + +#$ name: parents + +hg parents + +#$ name: older + +hg update 2 +hg parents +hg update + +#$ name: clone-push + +cd .. +hg clone hello hello-push + +#$ name: outgoing + +cd my-hello +hg outgoing ../hello-push + +#$ name: push + +hg push ../hello-push + +#$ name: push.nothing + +hg push ../hello-push + +#$ name: outgoing.net + +hg outgoing http://hg.serpentine.com/tutorial/hello + +#$ name: push.net + +hg push http://hg.serpentine.com/tutorial/hello + +#$ name: merge.clone + +cd .. +hg clone hello my-new-hello +cd my-new-hello +sed -i '/printf/i\\tprintf("once more, hello.\\n");' hello.c +hg commit -m 'A new hello for a new day.' + +#$ name: merge.cat + +cat hello.c +cat ../my-hello/hello.c + +#$ name: merge.pull + +hg pull ../my-hello + +#$ name: merge.heads + +hg heads + +#$ name: merge.update + +hg update + +#$ name: merge.merge + +hg merge + +#$ name: merge.parents + +hg parents +cat hello.c + +#$ name: merge.commit + +hg commit -m 'Merged changes' + +#$ name: merge.tip + +hg tip diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,72 @@ +#!/bin/bash + +hg init scam +cd scam + +#$ name: wife + +cat > letter.txt < letter.txt < letter.txt <]{7} /tmp/.* + +export HGMERGE=merge +hg merge +cat letter.txt + +#$ name: commit + +cat > letter.txt < letter.txt < \textbf{Greetings!} +> \textbf{I am Bryan O'Sullivan, no relation of the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m 'Send me your money'} +$ \textbf{hg tip} +changeset: +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Send me your money + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict.cousin.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.cousin.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam scam-cousin} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-cousin} +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Shehu Musa Abacha, cousin to the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m '419 scam, with cousin'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,17 @@ +$ \textbf{export HGMERGE=merge} +$ \textbf{hg merge} +merging letter.txt +merge: warning: conflicts during merge +merging letter.txt failed! +0 files updated, 0 files merged, 0 files removed, 1 files unresolved +There are unresolved merges, you can redo the full merge using: + hg update -C 1 + hg merge 2 +$ \textbf{cat letter.txt} +Greetings! + +I am Shehu Musa Abacha, cousin to the former +======= +I am Alhaji Abba Abacha, son of the former + +Nigerian dictator Sani Abacha. diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.pull.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam-cousin scam-merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-merge} +$ \textbf{hg pull -u ../scam-son} +pulling from ../scam-son +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +not updating, since new heads added +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict.son.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.son.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam scam-son} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-son} +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Alhaji Abba Abacha, son of the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m '419 scam, with son'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour-merge-conflict.wife.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.wife.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Mariam Abacha, the wife of former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg add letter.txt} +$ \textbf{hg commit -m '419 scam, first draft'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.clone-pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone-pull.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello hello-pull} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.clone-push.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone-push.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello hello-push} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg clone http://hg.serpentine.com/tutorial/hello} +destination directory: hello +requesting all changes +adding changesets +adding manifests +adding file changes +added 5 changesets with 5 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.commit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg commit} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.diff.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg diff} +diff -r hello.c + + +@@ -8,5 +8,6 @@ int main(int argc, char **argv) + int main(int argc, char **argv) + \{ + printf("hello, world!\textbackslash{}"); ++ printf("hello again!\textbackslash{}n"); + return 0; + \} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.help.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,20 @@ +$ \textbf{hg help init} +hg init [-e CMD] [--remotecmd CMD] [DEST] + +create a new repository in the given directory + + Initialize a new repository in the given directory. If the given + directory does not exist, it is created. + + If no directory is given, the current directory is used. + + It is possible to specify an ssh:// URL as the destination. + Look at the help text for the pull command for important details + about ssh:// URLs. + +options: + + -e --ssh specify ssh command to use + --remotecmd specify hg command to run on the remote side + +use "hg -v help init" to show global options diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.incoming.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.incoming.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd hello-pull} +$ \textbf{hg incoming ../my-hello} +comparing with ../my-hello +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.log-r.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-r.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,24 @@ +$ \textbf{hg log -r 3} +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +$ \textbf{hg log -r } +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +$ \textbf{hg log -r 1 -r 4} +changeset: +user: mpm@selenic.com + +summary: Create a makefile + +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.log-v.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-v.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg log -v -r 3} +changeset: +user: Bryan O'Sullivan + +files: Makefile +description: +Get make to generate the final binary from a .o file. + + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.log-vp.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-vp.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,21 @@ +$ \textbf{hg log -v -p -r 2} +changeset: +user: Bryan O'Sullivan + +files: hello.c +description: +Introduce a typo into hello.c. + + +diff -r -r hello.c + + +@@ -11,6 +11,6 @@ + + int main(int argc, char **argv) + \{ +- printf("hello, world!\textbackslash{}n"); ++ printf("hello, world!\textbackslash{}"); + return 0; + \} + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,27 @@ +$ \textbf{hg log} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +changeset: +user: mpm@selenic.com + +summary: Create a makefile + +changeset: +user: mpm@selenic.com + +summary: Create a standard "hello, world" program + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.log.range.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log.range.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,17 @@ +$ \textbf{hg log -r 2:4} +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.ls-a.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.ls-a.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd hello} +$ \textbf{ls -a} +. .. .hg Makefile hello.c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.ls.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.ls.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,5 @@ +$ \textbf{ls -l} +total 4 + +$ \textbf{ls hello} +Makefile hello.c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.cat.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +$ \textbf{cat hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("once more, hello.\textbackslash{}n"); + printf("hello, world!\textbackslash{}"); + return 0; +\} +$ \textbf{cat ../my-hello/hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); + return 0; +\} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.clone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello my-new-hello} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-new-hello} +$ \textbf{sed -i '/printf/i\textbackslash{}\textbackslash{}tprintf("once more, hello.\textbackslash{}\textbackslash{}n");' hello.c} +$ \textbf{hg commit -m 'A new hello for a new day.'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.commit.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg commit -m 'Merged changes'} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.heads.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.heads.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg heads} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: Added an extra line of output + +changeset: +user: Bryan O'Sullivan + +summary: A new hello for a new day. + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.merge.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg merge} +merging hello.c +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.parents.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,28 @@ +$ \textbf{hg parents} +changeset: +user: Bryan O'Sullivan + +summary: A new hello for a new day. + +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: Added an extra line of output + +$ \textbf{cat hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("once more, hello.\textbackslash{}n"); + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); + return 0; +\} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.pull.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg pull ../my-hello} +pulling from ../my-hello +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.tip.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg tip} +changeset: +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Merged changes + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.merge.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.update.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg update} +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.older.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.older.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg update 2} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +$ \textbf{hg update} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.outgoing.net.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.outgoing.net.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg outgoing http://hg.serpentine.com/tutorial/hello} +comparing with http://hg.serpentine.com/tutorial/hello +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.outgoing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.outgoing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd my-hello} +$ \textbf{hg outgoing ../hello-push} +comparing with ../hello-push +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.parents.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg parents} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.pull.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,22 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + +$ \textbf{hg pull ../my-hello} +pulling from ../my-hello +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy) +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.push.net.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.net.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg push http://hg.serpentine.com/tutorial/hello} +pushing to http://hg.serpentine.com/tutorial/hello +searching for changes +ssl required diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.push.nothing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.nothing.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg push ../hello-push} +pushing to ../hello-push +searching for changes +no changes found diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.push.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg push ../hello-push} +pushing to ../hello-push +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.reclone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.reclone.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello my-hello} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-hello} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.sed.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.sed.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1 @@ +$ \textbf{sed -i '/printf/a\textbackslash{}\textbackslash{}tprintf("hello again!\textbackslash{}\textbackslash{}n");' hello.c} diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.status.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +$ \textbf{ls} +Makefile hello.c +$ \textbf{hg status} +M hello.c diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.tip.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,21 @@ +$ \textbf{hg tip -vp} +changeset: +tag: tip +user: Bryan O'Sullivan + +files: hello.c +description: +Added an extra line of output + + +diff -r -r hello.c + + +@@ -8,5 +8,6 @@ int main(int argc, char **argv) + int main(int argc, char **argv) + \{ + printf("hello, world!\textbackslash{}"); ++ printf("hello again!\textbackslash{}n"); + return 0; + \} + diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.update.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,7 @@ +$ \textbf{grep printf hello.c} + printf("hello, world!\textbackslash{}"); +$ \textbf{hg update tip} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{grep printf hello.c} + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); diff -r 7be02466421b -r b0db5adf11c1 ja/examples/tour.version.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.version.out Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg version} +Mercurial Distributed SCM (version ) + +Copyright (C) 2005-2007 Matt Mackall and others +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -r 7be02466421b -r b0db5adf11c1 ja/fblinks --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/fblinks Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,67 @@ +#!/usr/bin/python + +import errno +import os +import re +import sys + +hg_id = sys.argv[1] + +dest_dir = sys.argv[2] + +empty_re = re.compile(r'^\s*$') +line_re = re.compile(r'^(\w+)(.*)') + +try: + os.makedirs(dest_dir) +except OSError, err: + if err.errno != errno.EEXIST: + raise + +def feedback(name, text, ctx_id): + return r'\marginpar{\scriptsize \href{http://demesne:8000/book/feedback/submit/%s/%s/%d/}{Feedback}}' % (hg_id, name, ctx_id) + +ctxs = {} +try: + cfp = open(os.path.join(dest_dir, 'rev-' + hg_id + '.ctx'), 'r+') + for line in cfp: + f, l, c = line.split(':', 2) + ctxs[(f, int(l))] = c.strip() +except IOError, err: + if err.errno != errno.ENOENT: raise + cfp = open(os.path.join(dest_dir, 'rev-' + hg_id + '.ctx'), 'w+') + +changes = 0 + +for name in sys.argv[3:]: + if not name.endswith('.tex'): + continue + dest_name = os.path.join(dest_dir, name) + ifp = open(name) + ofp = open(dest_name, 'w') + new_par = True + line_num = 0 + par_num = 0 + for line in ifp: + line_num += 1 + if new_par: + m = line_re.match(line) + if m: + par_num += 1 + ls = line.strip() + if ctxs.get((name, par_num)) != ls: + ctxs[(name, par_num)] = ls + changes += 1 + line = m.group(1) + feedback(name, line, par_num) + m.group(2) + new_par = False + elif not line.strip(): + new_par = True + ofp.write(line) + +if changes: + cfp.seek(0) + print '%s: %d changes' % (cfp.name, changes) + ctxs = ctxs.items() + ctxs.sort() + for ((file, line), content) in ctxs: + cfp.write('%s:%d: %s\n' % (file, line, content)) diff -r 7be02466421b -r b0db5adf11c1 ja/feature-branches.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/feature-branches.dot Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,8 @@ +digraph feature_branches { + master -> crypto; + master -> filesystems; + master -> ipc; + master -> memory; + master -> network; + master -> security; +} diff -r 7be02466421b -r b0db5adf11c1 ja/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/filelog.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 7be02466421b -r b0db5adf11c1 ja/filenames.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/filenames.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,306 @@ +\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 7be02466421b -r b0db5adf11c1 ja/fixhtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/fixhtml.py Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,50 @@ +#!/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 7be02466421b -r b0db5adf11c1 ja/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hgbook.css Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,441 @@ +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 7be02466421b -r b0db5adf11c1 ja/hgext.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hgext.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,429 @@ +\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 7be02466421b -r b0db5adf11c1 ja/hook.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hook.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1413 @@ +\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,\\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 7be02466421b -r b0db5adf11c1 ja/htlatex.book --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/htlatex.book Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,12 @@ +#!/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 7be02466421b -r b0db5adf11c1 ja/intro.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/intro.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,567 @@ +\chapter{Introduction} +\label{chap:intro} + +\section{About revision control} + +Revision control is the process of managing multiple versions of a +piece of information. In its simplest form, this is something that +many people do by hand: every time you modify a file, save it under a +new name that contains a number, each one higher than the number of +the preceding version. + +Manually managing multiple versions of even a single file is an +error-prone task, though, so software tools to help automate this +process have long been available. The earliest automated revision +control tools were intended to help a single user to manage revisions +of a single file. Over the past few decades, the scope of revision +control tools has expanded greatly; they now manage multiple files, +and help multiple people to work together. The best modern revision +control tools have no problem coping with thousands of people working +together on projects that consist of hundreds of thousands of files. + +\subsection{Why use revision control?} + +There are a number of reasons why you or your team might want to use +an automated revision control tool for a project. +\begin{itemize} +\item It will track the history and evolution of your project, so you + don't have to. For every change, you'll have a log of \emph{who} + made it; \emph{why} they made it; \emph{when} they made it; and + \emph{what} the change was. +\item When you're working with other people, revision control software + makes it easier for you to collaborate. For example, when people + more or less simultaneously make potentially incompatible changes, + the software will help you to identify and resolve those conflicts. +\item It can help you to recover from mistakes. If you make a change + that later turns out to be in error, you can revert to an earlier + version of one or more files. In fact, a \emph{really} good + revision control tool will even help you to efficiently figure out + exactly when a problem was introduced (see + section~\ref{sec:undo:bisect} for details). +\item It will help you to work simultaneously on, and manage the drift + between, multiple versions of your project. +\end{itemize} +Most of these reasons are equally valid---at least in theory---whether +you're working on a project by yourself, or with a hundred other +people. + +A key question about the practicality of revision control at these two +different scales (``lone hacker'' and ``huge team'') is how its +\emph{benefits} compare to its \emph{costs}. A revision control tool +that's difficult to understand or use is going to impose a high cost. + +A five-hundred-person project is likely to collapse under its own +weight almost immediately without a revision control tool and process. +In this case, the cost of using revision control might hardly seem +worth considering, since \emph{without} it, failure is almost +guaranteed. + +On the other hand, a one-person ``quick hack'' might seem like a poor +place to use a revision control tool, because surely the cost of using +one must be close to the overall cost of the project. Right? + +Mercurial uniquely supports \emph{both} of these scales of +development. You can learn the basics in just a few minutes, and due +to its low overhead, you can apply revision control to the smallest of +projects with ease. Its simplicity means you won't have a lot of +abstruse concepts or command sequences competing for mental space with +whatever you're \emph{really} trying to do. At the same time, +Mercurial's high performance and peer-to-peer nature let you scale +painlessly to handle large projects. + +No revision control tool can rescue a poorly run project, but a good +choice of tools can make a huge difference to the fluidity with which +you can work on a project. + +\subsection{The many names of revision control} + +Revision control is a diverse field, so much so that it doesn't +actually have a single name or acronym. Here are a few of the more +common names and acronyms you'll encounter: +\begin{itemize} +\item Revision control (RCS) +\item Software configuration management (SCM), or configuration management +\item Source code management +\item Source code control, or source control +\item Version control (VCS) +\end{itemize} +Some people claim that these terms actually have different meanings, +but in practice they overlap so much that there's no agreed or even +useful way to tease them apart. + +\section{A short history of revision control} + +The best known of the old-time revision control tools is SCCS (Source +Code Control System), which Marc Rochkind wrote at Bell Labs, in the +early 1970s. SCCS operated on individual files, and required every +person working on a project to have access to a shared workspace on a +single system. Only one person could modify a file at any time; +arbitration for access to files was via locks. It was common for +people to lock files, and later forget to unlock them, preventing +anyone else from modifying those files without the help of an +administrator. + +Walter Tichy developed a free alternative to SCCS in the early 1980s; +he called his program RCS (Revison Control System). Like SCCS, RCS +required developers to work in a single shared workspace, and to lock +files to prevent multiple people from modifying them simultaneously. + +Later in the 1980s, Dick Grune used RCS as a building block for a set +of shell scripts he initially called cmt, but then renamed to CVS +(Concurrent Versions System). The big innovation of CVS was that it +let developers work simultaneously and somewhat independently in their +own personal workspaces. The personal workspaces prevented developers +from stepping on each other's toes all the time, as was common with +SCCS and RCS. Each developer had a copy of every project file, and +could modify their copies independently. They had to merge their +edits prior to committing changes to the central repository. + +Brian Berliner took Grune's original scripts and rewrote them in~C, +releasing in 1989 the code that has since developed into the modern +version of CVS. CVS subsequently acquired the ability to operate over +a network connection, giving it a client/server architecture. CVS's +architecture is centralised; only the server has a copy of the history +of the project. Client workspaces just contain copies of recent +versions of the project's files, and a little metadata to tell them +where the server is. CVS has been enormously successful; it is +probably the world's most widely used revision control system. + +In the early 1990s, Sun Microsystems developed an early distributed +revision control system, called TeamWare. A TeamWare workspace +contains a complete copy of the project's history. TeamWare has no +notion of a central repository. (CVS relied upon RCS for its history +storage; TeamWare used SCCS.) + +As the 1990s progressed, awareness grew of a number of problems with +CVS. It records simultaneous changes to multiple files individually, +instead of grouping them together as a single logically atomic +operation. It does not manage its file hierarchy well; it is easy to +make a mess of a repository by renaming files and directories. Worse, +its source code is difficult to read and maintain, which made the +``pain level'' of fixing these architectural problems prohibitive. + +In 2001, Jim Blandy and Karl Fogel, two developers who had worked on +CVS, started a project to replace it with a tool that would have a +better architecture and cleaner code. The result, Subversion, does +not stray from CVS's centralised client/server model, but it adds +multi-file atomic commits, better namespace management, and a number +of other features that make it a generally better tool than CVS. +Since its initial release, it has rapidly grown in popularity. + +More or less simultaneously, Graydon Hoare began working on an +ambitious distributed revision control system that he named Monotone. +While Monotone addresses many of CVS's design flaws and has a +peer-to-peer architecture, it goes beyond earlier (and subsequent) +revision control tools in a number of innovative ways. It uses +cryptographic hashes as identifiers, and has an integral notion of +``trust'' for code from different sources. + +Mercurial began life in 2005. While a few aspects of its design are +influenced by Monotone, Mercurial focuses on ease of use, high +performance, and scalability to very large projects. + +\section{Trends in revision control} + +There has been an unmistakable trend in the development and use of +revision control tools over the past four decades, as people have +become familiar with the capabilities of their tools and constrained +by their limitations. + +The first generation began by managing single files on individual +computers. Although these tools represented a huge advance over +ad-hoc manual revision control, their locking model and reliance on a +single computer limited them to small, tightly-knit teams. + +The second generation loosened these constraints by moving to +network-centered architectures, and managing entire projects at a +time. As projects grew larger, they ran into new problems. With +clients needing to talk to servers very frequently, server scaling +became an issue for large projects. An unreliable network connection +could prevent remote users from being able to talk to the server at +all. As open source projects started making read-only access +available anonymously to anyone, people without commit privileges +found that they could not use the tools to interact with a project in +a natural way, as they could not record their changes. + +The current generation of revision control tools is peer-to-peer in +nature. All of these systems have dropped the dependency on a single +central server, and allow people to distribute their revision control +data to where it's actually needed. Collaboration over the Internet +has moved from constrained by technology to a matter of choice and +consensus. Modern tools can operate offline indefinitely and +autonomously, with a network connection only needed when syncing +changes with another repository. + +\section{A few of the advantages of distributed revision control} + +Even though distributed revision control tools have for several years +been as robust and usable as their previous-generation counterparts, +people using older tools have not yet necessarily woken up to their +advantages. There are a number of ways in which distributed tools +shine relative to centralised ones. + +For an individual developer, distributed tools are almost always much +faster than centralised tools. This is for a simple reason: a +centralised tool needs to talk over the network for many common +operations, because most metadata is stored in a single copy on the +central server. A distributed tool stores all of its metadata +locally. All else being equal, talking over the network adds overhead +to a centralised tool. Don't underestimate the value of a snappy, +responsive tool: you're going to spend a lot of time interacting with +your revision control software. + +Distributed tools are indifferent to the vagaries of your server +infrastructure, again because they replicate metadata to so many +locations. If you use a centralised system and your server catches +fire, you'd better hope that your backup media are reliable, and that +your last backup was recent and actually worked. With a distributed +tool, you have many backups available on every contributor's computer. + +The reliability of your network will affect distributed tools far less +than it will centralised tools. You can't even use a centralised tool +without a network connection, except for a few highly constrained +commands. With a distributed tool, if your network connection goes +down while you're working, you may not even notice. The only thing +you won't be able to do is talk to repositories on other computers, +something that is relatively rare compared with local operations. If +you have a far-flung team of collaborators, this may be significant. + +\subsection{Advantages for open source projects} + +If you take a shine to an open source project and decide that you +would like to start hacking on it, and that project uses a distributed +revision control tool, you are at once a peer with the people who +consider themselves the ``core'' of that project. If they publish +their repositories, you can immediately copy their project history, +start making changes, and record your work, using the same tools in +the same ways as insiders. By contrast, with a centralised tool, you +must use the software in a ``read only'' mode unless someone grants +you permission to commit changes to their central server. Until then, +you won't be able to record changes, and your local modifications will +be at risk of corruption any time you try to update your client's view +of the repository. + +\subsubsection{The forking non-problem} + +It has been suggested that distributed revision control tools pose +some sort of risk to open source projects because they make it easy to +``fork'' the development of a project. A fork happens when there are +differences in opinion or attitude between groups of developers that +cause them to decide that they can't work together any longer. Each +side takes a more or less complete copy of the project's source code, +and goes off in its own direction. + +Sometimes the camps in a fork decide to reconcile their differences. +With a centralised revision control system, the \emph{technical} +process of reconciliation is painful, and has to be performed largely +by hand. You have to decide whose revision history is going to +``win'', and graft the other team's changes into the tree somehow. +This usually loses some or all of one side's revision history. + +What distributed tools do with respect to forking is they make forking +the \emph{only} way to develop a project. Every single change that +you make is potentially a fork point. The great strength of this +approach is that a distributed revision control tool has to be really +good at \emph{merging} forks, because forks are absolutely +fundamental: they happen all the time. + +If every piece of work that everybody does, all the time, is framed in +terms of forking and merging, then what the open source world refers +to as a ``fork'' becomes \emph{purely} a social issue. If anything, +distributed tools \emph{lower} the likelihood of a fork: +\begin{itemize} +\item They eliminate the social distinction that centralised tools + impose: that between insiders (people with commit access) and + outsiders (people without). +\item They make it easier to reconcile after a social fork, because + all that's involved from the perspective of the revision control + software is just another merge. +\end{itemize} + +Some people resist distributed tools because they want to retain tight +control over their projects, and they believe that centralised tools +give them this control. 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{Advantages for commercial projects} + +Many commercial projects are undertaken by teams that are scattered +across the globe. Contributors who are far from a central server will +see slower command execution and perhaps less reliability. Commercial +revision control systems attempt to ameliorate these problems with +remote-site replication add-ons that are typically expensive to buy +and cantankerous to administer. A distributed system doesn't suffer +from these problems in the first place. Better yet, you can easily +set up multiple authoritative servers, say one per site, so that +there's no redundant communication between repositories over expensive +long-haul network links. + +Centralised revision control systems tend to have relatively low +scalability. It's not unusual for an expensive centralised system to +fall over under the combined load of just a few dozen concurrent +users. Once again, the typical response tends to be an expensive and +clunky replication facility. Since the load on a central server---if +you have one at all---is many times lower with a distributed +tool (because all of the data is replicated everywhere), a single +cheap server can handle the needs of a much larger team, and +replication to balance load becomes a simple matter of scripting. + +If you have an employee in the field, troubleshooting a problem at a +customer's site, they'll benefit from distributed revision control. +The tool will let them generate custom builds, try different fixes in +isolation from each other, and search efficiently through history for +the sources of bugs and regressions in the customer's environment, all +without needing to connect to your company's network. + +\section{Why choose Mercurial?} + +Mercurial has a unique set of properties that make it a particularly +good choice as a revision control system. +\begin{itemize} +\item It is easy to learn and use. +\item It is lightweight. +\item It scales excellently. +\item It is easy to customise. +\end{itemize} + +If you are at all familiar with revision control systems, you should +be able to get up and running with Mercurial in less than five +minutes. Even if not, it will take no more than a few minutes +longer. Mercurial's command and feature sets are generally uniform +and consistent, so you can keep track of a few general rules instead +of a host of exceptions. + +On a small project, you can start working with Mercurial in moments. +Creating new changes and branches; transferring changes around +(whether locally or over a network); and history and status operations +are all fast. Mercurial attempts to stay nimble and largely out of +your way by combining low cognitive overhead with blazingly fast +operations. + +The usefulness of Mercurial is not limited to small projects: it is +used by projects with hundreds to thousands of contributors, each +containing tens of thousands of files and hundreds of megabytes of +source code. + +If the core functionality of Mercurial is not enough for you, it's +easy to build on. Mercurial is well suited to scripting tasks, and +its clean internals and implementation in Python make it easy to add +features in the form of extensions. There are a number of popular and +useful extensions already available, ranging from helping to identify +bugs to improving performance. + +\section{Mercurial compared with other tools} + +Before you read on, please understand that this section necessarily +reflects my own experiences, interests, and (dare I say it) biases. I +have used every one of the revision control tools listed below, in +most cases for several years at a time. + + +\subsection{Subversion} + +Subversion is a popular revision control tool, developed to replace +CVS. It has a centralised client/server architecture. + +Subversion and Mercurial have similarly named commands for performing +the same operations, so if you're familiar with one, it is easy to +learn to use the other. Both tools are portable to all popular +operating systems. + +Subversion lacks a history-aware merge capability, forcing its users +to manually track exactly which revisions have been merged between +branches. If users fail to do this, or make mistakes, they face the +prospect of manually resolving merges with unnecessary conflicts. +Subversion also fails to merge changes when files or directories are +renamed. Subversion's poor merge support is its single biggest +weakness. + +Mercurial has a substantial performance advantage over Subversion on +every revision control operation I have benchmarked. I have measured +its advantage as ranging from a factor of two to a factor of six when +compared with Subversion~1.4.3's \emph{ra\_local} file store, which is +the fastest access method available). In more realistic deployments +involving a network-based store, Subversion will be at a substantially +larger disadvantage. Because many Subversion commands must talk to +the server and Subversion does not have useful replication facilities, +server capacity and network bandwidth become bottlenecks for modestly +large projects. + +Additionally, Subversion incurs substantial storage overhead to avoid +network transactions for a few common operations, such as finding +modified files (\texttt{status}) and displaying modifications against +the current revision (\texttt{diff}). As a result, a Subversion +working copy is often the same size as, or larger than, a Mercurial +repository and working directory, even though the Mercurial repository +contains a complete history of the project. + +Subversion is widely supported by third party tools. Mercurial +currently lags considerably in this area. This gap is closing, +however, and indeed some of Mercurial's GUI tools now outshine their +Subversion equivalents. Like Mercurial, Subversion has an excellent +user manual. + +Because Subversion doesn't store revision history on the client, it is +well suited to managing projects that deal with lots of large, opaque +binary files. If you check in fifty revisions to an incompressible +10MB file, Subversion's client-side space usage stays constant The +space used by any distributed SCM will grow rapidly in proportion to +the number of revisions, because the differences between each revision +are large. + +In addition, it's often difficult or, more usually, impossible to +merge different versions of a binary file. Subversion's ability to +let a user lock a file, so that they temporarily have the exclusive +right to commit changes to it, can be a significant advantage to a +project where binary files are widely used. + +Mercurial can import revision history from a Subversion repository. +It can also export revision history to a Subversion repository. This +makes it easy to ``test the waters'' and use Mercurial and Subversion +in parallel before deciding to switch. History conversion is +incremental, so you can perform an initial conversion, then small +additional conversions afterwards to bring in new changes. + + +\subsection{Git} + +Git is a distributed revision control tool that was developed for +managing the Linux kernel source tree. Like Mercurial, its early +design was somewhat influenced by Monotone. + +Git has a very large command set, with version~1.5.0 providing~139 +individual commands. It has something of a reputation for being +difficult to learn. Compared to Git, Mercurial has a strong focus on +simplicity. + +In terms of performance, Git is extremely fast. In several cases, it +is faster than Mercurial, at least on Linux, while Mercurial performs +better on other operations. However, on Windows, the performance and +general level of support that Git provides is, at the time of writing, +far behind that of Mercurial. + +While a Mercurial repository needs no maintenance, a Git repository +requires frequent manual ``repacks'' of its metadata. Without these, +performance degrades, while space usage grows rapidly. A server that +contains many Git repositories that are not rigorously and frequently +repacked will become heavily disk-bound during backups, and there have +been instances of daily backups taking far longer than~24 hours as a +result. A freshly packed Git repository is slightly smaller than a +Mercurial repository, but an unpacked repository is several orders of +magnitude larger. + +The core of Git is written in C. Many Git commands are implemented as +shell or Perl scripts, and the quality of these scripts varies widely. +I have encountered several instances where scripts charged along +blindly in the presence of errors that should have been fatal. + +Mercurial can import revision history from a Git repository. + + +\subsection{CVS} + +CVS is probably the most widely used revision control tool in the +world. Due to its age and internal untidiness, it has been only +lightly maintained for many years. + +It has a centralised client/server architecture. It does not group +related file changes into atomic commits, making it easy for people to +``break the build'': one person can successfully commit part of a +change and then be blocked by the need for a merge, causing other +people to see only a portion of the work they intended to do. This +also affects how you work with project history. If you want to see +all of the modifications someone made as part of a task, you will need +to manually inspect the descriptions and timestamps of the changes +made to each file involved (if you even know what those files were). + +CVS has a muddled notion of tags and branches that I will not attempt +to even describe. It does not support renaming of files or +directories well, making it easy to corrupt a repository. It has +almost no internal consistency checking capabilities, so it is usually +not even possible to tell whether or how a repository is corrupt. I +would not recommend CVS for any project, existing or new. + +Mercurial can import CVS revision history. However, there are a few +caveats that apply; these are true of every other revision control +tool's CVS importer, too. Due to CVS's lack of atomic changes and +unversioned filesystem hierarchy, it is not possible to reconstruct +CVS history completely accurately; some guesswork is involved, and +renames will usually not show up. Because a lot of advanced CVS +administration has to be done by hand and is hence error-prone, it's +common for CVS importers to run into multiple problems with corrupted +repositories (completely bogus revision timestamps and files that have +remained locked for over a decade are just two of the less interesting +problems I can recall from personal experience). + +Mercurial can import revision history from a CVS repository. + + +\subsection{Commercial tools} + +Perforce has a centralised client/server architecture, with no +client-side caching of any data. Unlike modern revision control +tools, Perforce requires that a user run a command to inform the +server about every file they intend to edit. + +The performance of Perforce is quite good for small teams, but it +falls off rapidly as the number of users grows beyond a few dozen. +Modestly large Perforce installations require the deployment of +proxies to cope with the load their users generate. + + +\subsection{Choosing a revision control tool} + +With the exception of CVS, all of the tools listed above have unique +strengths that suit them to particular styles of work. There is no +single revision control tool that is best in all situations. + +As an example, Subversion is a good choice for working with frequently +edited binary files, due to its centralised nature and support for +file locking. If you're averse to the command line, it currently has +better GUI support than other free revision control tools. However, +its poor merging is a substantial liability for busy projects with +overlapping development. + +I personally find Mercurial's properties of simplicity, performance, +and good merge support to be a compelling combination that has served +me well for several years. + + +\section{Switching from another tool to Mercurial} + +Mercurial is bundled with an extension named \hgext{convert}, which +can incrementally import revision history from several other revision +control tools. By ``incremental'', I mean that you can convert all of +a project's history to date in one go, then rerun the conversion later +to obtain new changes that happened after the initial conversion. + +The revision control tools supported by \hgext{convert} are as +follows: +\begin{itemize} +\item Subversion +\item CVS +\item Git +\item Darcs +\end{itemize} + +In addition, \hgext{convert} can export changes from Mercurial to +Subversion. This makes it possible to try Subversion and Mercurial in +parallel before committing to a switchover, without risking the loss +of any work. + +The \hgxcmd{conver}{convert} command is easy to use. Simply point it +at the path or URL of the source repository, optionally give it the +name of the destination repository, and it will start working. After +the initial conversion, just run the same command again to import new +changes. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 7be02466421b -r b0db5adf11c1 ja/kdiff3.png Binary file ja/kdiff3.png has changed diff -r 7be02466421b -r b0db5adf11c1 ja/license.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/license.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,138 @@ +\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 7be02466421b -r b0db5adf11c1 ja/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/metadata.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Changelog + Manifest + Filelogs + + diff -r 7be02466421b -r b0db5adf11c1 ja/mq-collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq-collab.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,393 @@ +\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 +othter 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 +qdetermining 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 7be02466421b -r b0db5adf11c1 ja/mq-ref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq-ref.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,353 @@ +\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{\hgxcmd{mq}{qversion}} + +The \hgxcmd{mq}{qversion} command prints the version of MQ that is in use. + +\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 7be02466421b -r b0db5adf11c1 ja/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq-stack.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/mq.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,1043 @@ +\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 five 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 7be02466421b -r b0db5adf11c1 ja/note.png Binary file ja/note.png has changed diff -r 7be02466421b -r b0db5adf11c1 ja/preface.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/preface.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,67 @@ +\chapter*{Preface} +\addcontentsline{toc}{chapter}{Preface} +\label{chap:preface} + +Distributed revision control is a relatively new territory, and has +thus far grown due to people's willingness to strike out into +ill-charted territory. + +I am writing a book about distributed revision control because I +believe that it is an important subject that deserves a field guide. +I chose to write about Mercurial because it is the easiest tool to +learn the terrain with, and yet it scales to the demands of real, +challenging environments where many other revision control tools fail. + +\section{This book is a work in progress} + +I am releasing this book while I am still writing it, in the hope that +it will prove useful to others. I also hope that readers will +contribute as they see fit. + +\section{About the examples in this book} + +This book takes an unusual approach to code samples. Every example is +``live''---each one is actually the result of a shell script that +executes the Mercurial commands you see. Every time an image of the +book is built from its sources, all the example scripts are +automatically run, and their current results compared against their +expected results. + +The advantage of this approach is that the examples are always +accurate; they describe \emph{exactly} the behaviour of the version of +Mercurial that's mentioned at the front of the book. If I update the +version of Mercurial that I'm documenting, and the output of some +command changes, the build fails. + +There is a small disadvantage to this approach, which is that the +dates and times you'll see in examples tend to be ``squashed'' +together in a way that they wouldn't be if the same commands were +being typed by a human. Where a human can issue no more than one +command every few seconds, with any resulting timestamps +correspondingly spread out, my automated example scripts run many +commands in one second. + +As an instance of this, several consecutive commits in an example can +show up as having occurred during the same second. You can see this +occur in the \hgext{bisect} example in section~\ref{sec:undo:bisect}, +for instance. + +So when you're reading examples, don't place too much weight on the +dates or times you see in the output of commands. But \emph{do} be +confident that the behaviour you're seeing is consistent and +reproducible. + +\section{Colophon---this book is Free} + +This book is licensed under the Open Publication License, and is +produced entirely using Free Software tools. It is typeset with +\LaTeX{}; illustrations are drawn and rendered with +\href{http://www.inkscape.org/}{Inkscape}. + +The complete source code for this book is published as a Mercurial +repository, at \url{http://hg.serpentine.com/mercurial/book}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 7be02466421b -r b0db5adf11c1 ja/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/revlog.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/snapshot.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/srcinstall.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/srcinstall.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,53 @@ +\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 7be02466421b -r b0db5adf11c1 ja/template.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/template.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,475 @@ +\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-byte 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 items it is passed. +\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 +``\texttt{\\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 7be02466421b -r b0db5adf11c1 ja/tour-basic.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-basic.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,617 @@ +\chapter{A tour of Mercurial: the basics} +\label{chap:tour-basic} + +\section{Installing Mercurial on your system} +\label{sec:tour:install} + +Prebuilt binary packages of Mercurial are available for every popular +operating system. These make it easy to start using Mercurial on your +computer immediately. + +\subsection{Linux} + +Because each Linux distribution has its own packaging tools, policies, +and rate of development, it's difficult to give a comprehensive set of +instructions on how to install Mercurial binaries. The version of +Mercurial that you will end up with can vary depending on how active +the person is who maintains the package for your distribution. + +To keep things simple, I will focus on installing Mercurial from the +command line under the most popular Linux distributions. Most of +these distributions provide graphical package managers that will let +you install Mercurial with a single click; the package name to look +for is \texttt{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] Ubuntu's Mercurial package is based on Debian's. To + install it, run the following command. + \begin{codesample4} + apt-get install mercurial + \end{codesample4} + The Ubuntu package for Mercurial tends to lag behind the Debian + version by a considerable time margin (at the time of writing, seven + months), which in some cases will mean that on Ubuntu, you may run + into problems that have since been fixed in the Debian package. +\end{itemize} + +\subsection{Solaris} + +XXX. + +\subsection{Mac OS X} + +Lee Cantey publishes an installer of Mercurial for Mac OS~X at +\url{http://mercurial.berkwood.com}. This package works on both +Intel-~and Power-based Macs. Before you can use it, you must install +a compatible version of Universal MacPython~\cite{web:macpython}. This +is easy to do; simply follow the instructions on Lee's site. + +\subsection{Windows} + +Lee Cantey also publishes an installer of Mercurial for Windows at +\url{http://mercurial.berkwood.com}. This package has no external +dependencies; it ``just works''. + +\begin{note} + The Windows version of Mercurial does not automatically convert line + endings between Windows and Unix styles. If you want to share work + with Unix users, you must do a little additional configuration + work. XXX Flesh this out. +\end{note} + +\section{Getting started} + +To begin, we'll use the \hgcmd{version} command to find out whether +Mercurial is actually installed properly. The actual version +information that it prints isn't so important; it's whether it prints +anything at all that we care about. +\interaction{tour.version} + +\subsection{Built-in help} + +Mercurial provides a built-in help system. This is invaluable for those +times when you find yourself stuck trying to remember how to run a +command. If you are completely stuck, simply run \hgcmd{help}; it +will print a brief list of commands, along with a description of what +each does. If you ask for help on a specific command (as below), it +prints more detailed information. +\interaction{tour.help} +For a more impressive level of detail (which you won't usually need) +run \hgcmdargs{help}{\hggopt{-v}}. The \hggopt{-v} option is short +for \hggopt{--verbose}, and tells Mercurial to print more information +than it usually would. + +\section{Working with a repository} + +In Mercurial, everything happens inside a \emph{repository}. The +repository for a project contains all of the files that ``belong to'' +that project, along with a historical record of the project's files. + +There's nothing particularly magical about a repository; it is simply +a directory tree in your filesystem that Mercurial treats as special. +You can rename or delete a repository any time you like, using either the +command line or your file browser. + +\subsection{Making a local copy of a repository} + +\emph{Copying} a repository is just a little bit special. While you +could use a normal file copying command to make a copy of a +repository, it's best to use a built-in command that Mercurial +provides. This command is called \hgcmd{clone}, because it creates an +identical copy of an existing repository. +\interaction{tour.clone} +If our clone succeeded, we should now have a local directory called +\dirname{hello}. This directory will contain some files. +\interaction{tour.ls} +These files have the same contents and history in our repository as +they do in the repository we cloned. + +Every Mercurial repository is complete, self-contained, and +independent. It contains its own private copy of a project's files +and history. A cloned repository remembers the location of the +repository it was cloned from, but it does not communicate with that +repository, or any other, unless you tell it to. + +What this means for now is that we're free to experiment with our +repository, safe in the knowledge that it's a private ``sandbox'' that +won't affect anyone else. + +\subsection{What's in a repository?} + +When we take a more detailed look inside a repository, we can see that +it contains a directory named \dirname{.hg}. This is where Mercurial +keeps all of its metadata for the repository. +\interaction{tour.ls-a} + +The contents of the \dirname{.hg} directory and its subdirectories are +private to Mercurial. Every other file and directory in the +repository is yours to do with as you please. + +To introduce a little terminology, the \dirname{.hg} directory is the +``real'' repository, and all of the files and directories that coexist +with it are said to live in the \emph{working directory}. An easy way +to remember the distinction is that the \emph{repository} contains the +\emph{history} of your project, while the \emph{working directory} +contains a \emph{snapshot} of your project at a particular point in +history. + +\section{A tour through history} + +One of the first things we might want to do with a new, unfamiliar +repository is understand its history. The \hgcmd{log} command gives +us a view of history. +\interaction{tour.log} +By default, this command prints a brief paragraph of output for each +change to the project that was recorded. In Mercurial terminology, we +call each of these recorded events a \emph{changeset}, because it can +contain a record of changes to several files. + +The fields in a record of output from \hgcmd{log} are as follows. +\begin{itemize} +\item[\texttt{changeset}] This field has the format of a number, + followed by a colon, followed by a hexadecimal string. These are + \emph{identifiers} for the changeset. There are two identifiers + because the number is shorter and easier to type than the hex + string. +\item[\texttt{user}] The identity of the person who created the + changeset. This is a free-form field, but it most often contains a + person's name and email address. +\item[\texttt{date}] The date and time on which the changeset was + created, and the timezone in which it was created. (The date and + time are local to that timezone; they display what time and date it + was for the person who created the changeset.) +\item[\texttt{summary}] The first line of the text message that the + creator of the changeset entered to describe the changeset. +\end{itemize} +The default output printed by \hgcmd{log} is purely a summary; it is +missing a lot of detail. + +Figure~\ref{fig:tour-basic:history} provides a graphical representation of +the history of the \dirname{hello} repository, to make it a little +easier to see which direction history is ``flowing'' in. We'll be +returning to this figure several times in this chapter and the chapter +that follows. + +\begin{figure}[ht] + \centering + \grafix{tour-history} + \caption{Graphical history of the \dirname{hello} repository} + \label{fig:tour-basic:history} +\end{figure} + +\subsection{Changesets, revisions, and talking to other + people} + +As English is a notoriously sloppy language, and computer science has +a hallowed history of terminological confusion (why use one term when +four will do?), revision control has a variety of words and phrases +that mean the same thing. If you are talking about Mercurial history +with other people, you will find that the word ``changeset'' is often +compressed to ``change'' or (when written) ``cset'', and sometimes a +changeset is referred to as a ``revision'' or a ``rev''. + +While it doesn't matter what \emph{word} you use to refer to the +concept of ``a~changeset'', the \emph{identifier} that you use to +refer to ``a~\emph{specific} changeset'' is of great importance. +Recall that the \texttt{changeset} field in the output from +\hgcmd{log} identifies a changeset using both a number and a +hexadecimal string. +\begin{itemize} +\item The revision number is \emph{only valid in that repository}, +\item while the hex string is the \emph{permanent, unchanging + identifier} that will always identify that exact changeset in + \emph{every} copy of the repository. +\end{itemize} +This distinction is important. If you send someone an email talking +about ``revision~33'', there's a high likelihood that their +revision~33 will \emph{not be the same} as yours. The reason for this +is that a revision number depends on the order in which changes +arrived in a repository, and there is no guarantee that the same +changes will happen in the same order in different repositories. +Three changes $a,b,c$ can easily appear in one repository as $0,1,2$, +while in another as $1,0,2$. + +Mercurial uses revision numbers purely as a convenient shorthand. If +you need to discuss a changeset with someone, or make a record of a +changeset for some other reason (for example, in a bug report), use +the hexadecimal identifier. + +\subsection{Viewing specific revisions} + +To narrow the output of \hgcmd{log} down to a single revision, use the +\hgopt{log}{-r} (or \hgopt{log}{--rev}) option. You can use either a +revision number or a long-form changeset identifier, and you can +provide as many revisions as you want. \interaction{tour.log-r} + +If you want to see the history of several revisions without having to +list each one, you can use \emph{range notation}; this lets you +express the idea ``I want all revisions between $a$ and $b$, +inclusive''. +\interaction{tour.log.range} +Mercurial also honours the order in which you specify revisions, so +\hgcmdargs{log}{-r 2:4} prints $2,3,4$ while \hgcmdargs{log}{-r 4:2} +prints $4,3,2$. + +\subsection{More detailed information} + +While the summary information printed by \hgcmd{log} is useful if you +already know what you're looking for, you may need to see a complete +description of the change, or a list of the files changed, if you're +trying to decide whether a changeset is the one you're looking for. +The \hgcmd{log} command's \hggopt{-v} (or \hggopt{--verbose}) +option gives you this extra detail. +\interaction{tour.log-v} + +If you want to see both the description and content of a change, add +the \hgopt{log}{-p} (or \hgopt{log}{--patch}) option. This displays +the content of a change as a \emph{unified diff} (if you've never seen +a unified diff before, see section~\ref{sec:mq:patch} for an overview). +\interaction{tour.log-vp} + +\section{All about command options} + +Let's take a brief break from exploring Mercurial commands to discuss +a pattern in the way that they work; you may find this useful to keep +in mind as we continue our tour. + +Mercurial has a consistent and straightforward approach to dealing +with the options that you can pass to commands. It follows the +conventions for options that are common to modern Linux and Unix +systems. +\begin{itemize} +\item Every option has a long name. For example, as we've already + seen, the \hgcmd{log} command accepts a \hgopt{log}{--rev} option. +\item Most options have short names, too. Instead of + \hgopt{log}{--rev}, we can use \hgopt{log}{-r}. (The reason that + some options don't have short names is that the options in question + are rarely used.) +\item Long options start with two dashes (e.g.~\hgopt{log}{--rev}), + while short options start with one (e.g.~\hgopt{log}{-r}). +\item Option naming and usage is consistent across commands. For + example, every command that lets you specify a changeset~ID or + revision number accepts both \hgopt{log}{-r} and \hgopt{log}{--rev} + arguments. +\end{itemize} +In the examples throughout this book, I use short options instead of +long. This just reflects my own preference, so don't read anything +significant into it. + +Most commands that print output of some kind will print more output +when passed a \hggopt{-v} (or \hggopt{--verbose}) option, and less +when passed \hggopt{-q} (or \hggopt{--quiet}). + +\section{Making and reviewing changes} + +Now that we have a grasp of viewing history in Mercurial, let's take a +look at making some changes and examining them. + +The first thing we'll do is isolate our experiment in a repository of +its own. We use the \hgcmd{clone} command, but we don't need to +clone a copy of the remote repository. Since we already have a copy +of it locally, we can just clone that instead. This is much faster +than cloning over the network, and cloning a local repository uses +less disk space in most cases, too. +\interaction{tour.reclone} +As an aside, it's often good practice to keep a ``pristine'' copy of a +remote repository around, which you can then make temporary clones of +to create sandboxes for each task you want to work on. This lets you +work on multiple tasks in parallel, each isolated from the others +until it's complete and you're ready to integrate it back. Because +local clones are so cheap, there's almost no overhead to cloning and +destroying repositories whenever you want. + +In our \dirname{my-hello} repository, we have a file +\filename{hello.c} that contains the classic ``hello, world'' program. +Let's use the ancient and venerable \command{sed} command to edit this +file so that it prints a second line of output. (I'm only using +\command{sed} to do this because it's easy to write a scripted example +this way. Since you're not under the same constraint, you probably +won't want to use \command{sed}; simply use your preferred text editor to +do the same thing.) +\interaction{tour.sed} + +Mercurial's \hgcmd{status} command will tell us what Mercurial knows +about the files in the repository. +\interaction{tour.status} +The \hgcmd{status} command prints no output for some files, but a line +starting with ``\texttt{M}'' for \filename{hello.c}. Unless you tell +it to, \hgcmd{status} will not print any output for files that have +not been modified. + +The ``\texttt{M}'' indicates that Mercurial has noticed that we +modified \filename{hello.c}. We didn't need to \emph{inform} +Mercurial that we were going to modify the file before we started, or +that we had modified the file after we were done; it was able to +figure this out itself. + +It's a little bit helpful to know that we've modified +\filename{hello.c}, but we might prefer to know exactly \emph{what} +changes we've made to it. To do this, we use the \hgcmd{diff} +command. +\interaction{tour.diff} + +\section{Recording changes in a new changeset} + +We can modify files, build and test our changes, and use +\hgcmd{status} and \hgcmd{diff} to review our changes, until we're +satisfied with what we've done and arrive at a natural stopping point +where we want to record our work in a new changeset. + +The \hgcmd{commit} command lets us create a new changeset; we'll +usually refer to this as ``making a commit'' or ``committing''. + +\subsection{Setting up a username} + +When you try to run \hgcmd{commit} for the first time, it is not +guaranteed to succeed. Mercurial records your name and address with +each change that you commit, so that you and others will later be able +to tell who made each change. Mercurial tries to automatically figure +out a sensible username to commit the change with. It will attempt +each of the following methods, in order: +\begin{enumerate} +\item If you specify a \hgopt{commit}{-u} option to the \hgcmd{commit} + command on the command line, followed by a username, this is always + given the highest precedence. +\item If you have set the \envar{HGUSER} environment variable, this is + checked next. +\item If you create a file in your home directory called + \sfilename{.hgrc}, with a \rcitem{ui}{username} entry, that will be + used next. To see what the contents of this file should look like, + refer to section~\ref{sec:tour-basic:username} below. +\item If you have set the \envar{EMAIL} environment variable, this + will be used next. +\item Mercurial will query your system to find out your local user + name and host name, and construct a username from these components. + Since this often results in a username that is not very useful, it + will print a warning if it has to do this. +\end{enumerate} +If all of these mechanisms fail, Mercurial will fail, printing an +error message. In this case, it will not let you commit until you set +up a username. + +You should think of the \envar{HGUSER} environment variable and the +\hgopt{commit}{-u} option to the \hgcmd{commit} command as ways to +\emph{override} Mercurial's default selection of username. For normal +use, the simplest and most robust way to set a username for yourself +is by creating a \sfilename{.hgrc} file; see below for details. + +\subsubsection{Creating a Mercurial configuration file} +\label{sec:tour-basic:username} + +To set a user name, use your favourite editor to create a file called +\sfilename{.hgrc} in your home directory. Mercurial will use this +file to look up your personalised configuration settings. The initial +contents of your \sfilename{.hgrc} should look like this. +\begin{codesample2} + # This is a Mercurial configuration file. + [ui] + username = Firstname Lastname +\end{codesample2} +The ``\texttt{[ui]}'' line begins a \emph{section} of the config file, +so you can read the ``\texttt{username = ...}'' line as meaning ``set +the value of the \texttt{username} item in the \texttt{ui} section''. +A section continues until a new section begins, or the end of the +file. Mercurial ignores empty lines and treats any text from +``\texttt{\#}'' to the end of a line as a comment. + +\subsubsection{Choosing a user name} + +You can use any text you like as the value of the \texttt{username} +config item, since this information is for reading by other people, +but for interpreting by Mercurial. The convention that most people +follow is to use their name and email address, as in the example +above. + +\begin{note} + Mercurial's built-in web server obfuscates email addresses, to make + it more difficult for the email harvesting tools that spammers use. + This reduces the likelihood that you'll start receiving more junk + email if you publish a Mercurial repository on the web. +\end{note} + +\subsection{Writing a commit message} + +When we commit a change, Mercurial drops us into a text editor, to +enter a message that will describe the modifications we've made in +this changeset. This is called the \emph{commit message}. It will be +a record for readers of what we did and why, and it will be printed by +\hgcmd{log} after we've finished committing. +\interaction{tour.commit} + +The editor that the \hgcmd{commit} command drops us into will contain +an empty line, followed by a number of lines starting with +``\texttt{HG:}''. +\begin{codesample2} + \emph{empty line} + HG: changed hello.c +\end{codesample2} +Mercurial ignores the lines that start with ``\texttt{HG:}''; it uses +them only to tell us which files it's recording changes to. Modifying +or deleting these lines has no effect. + +\subsection{Writing a good commit message} + +Since \hgcmd{log} only prints the first line of a commit message by +default, it's best to write a commit message whose first line stands +alone. Here's a real example of a commit message that \emph{doesn't} +follow this guideline, and hence has a summary that is not readable. +\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} + +As far as the remainder of the contents of the commit message are +concerned, there are no hard-and-fast rules. Mercurial itself doesn't +interpret or care about the contents of the commit message, though +your project may have policies that dictate a certain kind of +formatting. + +My personal preference is for short, but informative, commit messages +that tell me something that I can't figure out with a quick glance at +the output of \hgcmdargs{log}{--patch}. + +\subsection{Aborting a commit} + +If you decide that you don't want to commit while in the middle of +editing a commit message, simply exit from your editor without saving +the file that it's editing. This will cause nothing to happen to +either the repository or the working directory. + +If we run the \hgcmd{commit} command without any arguments, it records +all of the changes we've made, as reported by \hgcmd{status} and +\hgcmd{diff}. + +\subsection{Admiring our new handiwork} + +Once we've finished the commit, we can use the \hgcmd{tip} command to +display the changeset we just created. This command produces output +that is identical to \hgcmd{log}, but it only displays the newest +revision in the repository. +\interaction{tour.tip} +We refer to the newest revision in the repository as the tip revision, +or simply the tip. + +\section{Sharing changes} + +We mentioned earlier that repositories in Mercurial are +self-contained. This means that the changeset we just created exists +only in our \dirname{my-hello} repository. Let's look at a few ways +that we can propagate this change into other repositories. + +\subsection{Pulling changes from another repository} +\label{sec:tour:pull} + +To get started, let's clone our original \dirname{hello} repository, +which does not contain the change we just committed. We'll call our +temporary repository \dirname{hello-pull}. +\interaction{tour.clone-pull} + +We'll use the \hgcmd{pull} command to bring changes from +\dirname{my-hello} into \dirname{hello-pull}. However, blindly +pulling unknown changes into a repository is a somewhat scary +prospect. Mercurial provides the \hgcmd{incoming} command to tell us +what changes the \hgcmd{pull} command \emph{would} pull into the +repository, without actually pulling the changes in. +\interaction{tour.incoming} +(Of course, someone could cause more changesets to appear in the +repository that we ran \hgcmd{incoming} in, before we get a chance to +\hgcmd{pull} the changes, so that we could end up pulling changes that we +didn't expect.) + +Bringing changes into a repository is a simple matter of running the +\hgcmd{pull} command, and telling it which repository to pull from. +\interaction{tour.pull} +As you can see from the before-and-after output of \hgcmd{tip}, we +have successfully pulled changes into our repository. There remains +one step before we can see these changes in the working directory. + +\subsection{Updating the working directory} + +We have so far glossed over the relationship between a repository and +its working directory. The \hgcmd{pull} command that we ran in +section~\ref{sec:tour:pull} brought changes into the repository, but +if we check, there's no sign of those changes in the working +directory. This is because \hgcmd{pull} does not (by default) touch +the working directory. Instead, we use the \hgcmd{update} command to +do this. +\interaction{tour.update} + +It might seem a bit strange that \hgcmd{pull} doesn't update the +working directory automatically. There's actually a good reason for +this: you can use \hgcmd{update} to update the working directory to +the state it was in at \emph{any revision} in the history of the +repository. If you had the working directory updated to an old +revision---to hunt down the origin of a bug, say---and ran a +\hgcmd{pull} which automatically updated the working directory to a +new revision, you might not be terribly happy. + +However, since pull-then-update is such a common thing to do, +Mercurial lets you combine the two by passing the \hgopt{pull}{-u} +option to \hgcmd{pull}. +\begin{codesample2} + hg pull -u +\end{codesample2} +If you look back at the output of \hgcmd{pull} in +section~\ref{sec:tour:pull} when we ran it without \hgopt{pull}{-u}, +you can see that it printed a helpful reminder that we'd have to take +an explicit step to update the working directory: +\begin{codesample2} + (run 'hg update' to get a working copy) +\end{codesample2} + +To find out what revision the working directory is at, use the +\hgcmd{parents} command. +\interaction{tour.parents} +If you look back at figure~\ref{fig:tour-basic:history}, you'll see +arrows connecting each changeset. The node that the arrow leads +\emph{from} in each case is a parent, and the node that the arrow +leads \emph{to} is its child. The working directory has a parent in +just the same way; this is the changeset that the working directory +currently contains. + +To update the working directory to a particular revision, give a +revision number or changeset~ID to the \hgcmd{update} command. +\interaction{tour.older} +If you omit an explicit revision, \hgcmd{update} will update to the +tip revision, as shown by the second call to \hgcmd{update} in the +example above. + +\subsection{Pushing changes to another repository} + +Mercurial lets us push changes to another repository, from the +repository we're currently visiting. As with the example of +\hgcmd{pull} above, we'll create a temporary repository to push our +changes into. +\interaction{tour.clone-push} +The \hgcmd{outgoing} command tells us what changes would be pushed +into another repository. +\interaction{tour.outgoing} +And the \hgcmd{push} command does the actual push. +\interaction{tour.push} +As with \hgcmd{pull}, the \hgcmd{push} command does not update the +working directory in the repository that it's pushing changes into. +(Unlike \hgcmd{pull}, \hgcmd{push} does not provide a \texttt{-u} +option that updates the other repository's working directory.) + +What happens if we try to pull or push changes and the receiving +repository already has those changes? Nothing too exciting. +\interaction{tour.push.nothing} + +\subsection{Sharing changes over a network} + +The commands we have covered in the previous few sections are not +limited to working with local repositories. Each works in exactly the +same fashion over a network connection; simply pass in a URL instead +of a local path. +\interaction{tour.outgoing.net} +In this example, we can see what changes we could push to the remote +repository, but the repository is understandably not set up to let +anonymous users push to it. +\interaction{tour.push.net} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 7be02466421b -r b0db5adf11c1 ja/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-history.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + (newest) + (oldest) + + 4: b57f + + + revisionnumber + changesetidentifier + + diff -r 7be02466421b -r b0db5adf11c1 ja/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-conflict.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-merge.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 4: b57f + + + 5: ae13 + + + 6: d2b5 + + tip (and head) + head + + + + merge + working directoryduring merge + + 4: b57f + + + 5: ae13 + + + 6: d2b5 + + tip + + + 7: dba3 + Working directory during merge + Repository after merge committed + + diff -r 7be02466421b -r b0db5adf11c1 ja/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-pull.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: ae13 + + + 6: d2b5 + + tip (and head) + head + + diff -r 7be02466421b -r b0db5adf11c1 ja/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-sep-repos.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,466 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: ae13 + + my-hello + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: d2b5 + + my-new-hello + newest changesdiffer + common history + + + + + + + head revision(has no children) + + diff -r 7be02466421b -r b0db5adf11c1 ja/tour-merge.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,283 @@ +\chapter{A tour of Mercurial: merging work} +\label{chap:tour-merge} + +We've now covered cloning a repository, making changes in a +repository, and pulling or pushing changes from one repository into +another. Our next step is \emph{merging} changes from separate +repositories. + +\section{Merging streams of work} + +Merging is a fundamental part of working with a distributed revision +control tool. +\begin{itemize} +\item Alice and Bob each have a personal copy of a repository for a + project they're collaborating on. Alice fixes a bug in her + repository; Bob adds a new feature in his. They want the shared + repository to contain both the bug fix and the new feature. +\item I frequently work on several different tasks for a single + project at once, each safely isolated in its own repository. + Working this way means that I often need to merge one piece of my + own work with another. +\end{itemize} + +Because merging is such a common thing to need to do, Mercurial makes +it easy. Let's walk through the process. We'll begin by cloning yet +another repository (see how often they spring up?) and making a change +in it. +\interaction{tour.merge.clone} +We should now have two copies of \filename{hello.c} with different +contents. The histories of the two repositories have also diverged, +as illustrated in figure~\ref{fig:tour-merge:sep-repos}. +\interaction{tour.merge.cat} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-sep-repos} + \caption{Divergent recent histories of the \dirname{my-hello} and + \dirname{my-new-hello} repositories} + \label{fig:tour-merge:sep-repos} +\end{figure} + +We already know that pulling changes from our \dirname{my-hello} +repository will have no effect on the working directory. +\interaction{tour.merge.pull} +However, the \hgcmd{pull} command says something about ``heads''. + +\subsection{Head changesets} + +A head is a change that has no descendants, or children, as they're +also known. The tip revision is thus a head, because the newest +revision in a repository doesn't have any children, but a repository +can contain more than one head. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-pull} + \caption{Repository contents after pulling from \dirname{my-hello} into + \dirname{my-new-hello}} + \label{fig:tour-merge:pull} +\end{figure} + +In figure~\ref{fig:tour-merge:pull}, you can see the effect of the +pull from \dirname{my-hello} into \dirname{my-new-hello}. The history +that was already present in \dirname{my-new-hello} is untouched, but a +new revision has been added. By referring to +figure~\ref{fig:tour-merge:sep-repos}, we can see that the +\emph{changeset ID} remains the same in the new repository, but the +\emph{revision number} has changed. (This, incidentally, is a fine +example of why it's not safe to use revision numbers when discussing +changesets.) We can view the heads in a repository using the +\hgcmd{heads} command. +\interaction{tour.merge.heads} + +\subsection{Performing the merge} + +What happens if we try to use the normal \hgcmd{update} command to +update to the new tip? +\interaction{tour.merge.update} +Mercurial is telling us that the \hgcmd{update} command won't do a +merge; it won't update the working directory when it thinks we might +be wanting to do a merge, unless we force it to do so. Instead, we +use the \hgcmd{merge} command to merge the two heads. +\interaction{tour.merge.merge} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-merge} + \caption{Working directory and repository during merge, and + following commit} + \label{fig:tour-merge:merge} +\end{figure} + +This updates the working directory so that it contains changes from +\emph{both} heads, which is reflected in both the output of +\hgcmd{parents} and the contents of \filename{hello.c}. +\interaction{tour.merge.parents} + +\subsection{Committing the results of the merge} + +Whenever we've done a merge, \hgcmd{parents} will display two parents +until we \hgcmd{commit} the results of the merge. +\interaction{tour.merge.commit} +We now have a new tip revision; notice that it has \emph{both} of +our former heads as its parents. These are the same revisions that +were previously displayed by \hgcmd{parents}. +\interaction{tour.merge.tip} +In figure~\ref{fig:tour-merge:merge}, you can see a representation of +what happens to the working directory during the merge, and how this +affects the repository when the commit happens. During the merge, the +working directory has two parent changesets, and these become the +parents of the new changeset. + +\section{Merging conflicting changes} + +Most merges are simple affairs, but sometimes you'll find yourself +merging changes where each modifies the same portions of the same +files. Unless both modifications are identical, this results in a +\emph{conflict}, where you have to decide how to reconcile the +different changes into something coherent. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-conflict} + \caption{Conflicting changes to a document} + \label{fig:tour-merge:conflict} +\end{figure} + +Figure~\ref{fig:tour-merge:conflict} illustrates an instance of two +conflicting changes to a document. We started with a single version +of the file; then we made some changes; while someone else made +different changes to the same text. Our task in resolving the +conflicting changes is to decide what the file should look like. + +Mercurial doesn't have a built-in facility for handling conflicts. +Instead, it runs an external program called \command{hgmerge}. This +is a shell script that is bundled with Mercurial; you can change it to +behave however you please. What it does by default is try to find one +of several different merging tools that are likely to be installed on +your system. It first tries a few fully automatic merging tools; if +these don't succeed (because the resolution process requires human +guidance) or aren't present, the script tries a few different +graphical merging tools. + +It's also possible to get Mercurial to run another program or script +instead of \command{hgmerge}, by setting the \envar{HGMERGE} +environment variable to the name of your preferred program. + +\subsection{Using a graphical merge tool} + +My preferred graphical merge tool is \command{kdiff3}, which I'll use +to describe the features that are common to graphical file merging +tools. You can see a screenshot of \command{kdiff3} in action in +figure~\ref{fig:tour-merge:kdiff3}. The kind of merge it is +performing is called a \emph{three-way merge}, because there are three +different versions of the file of interest to us. The tool thus +splits the upper portion of the window into three panes: +\begin{itemize} +\item At the left is the \emph{base} version of the file, i.e.~the + most recent version from which the two versions we're trying to + merge are descended. +\item In the middle is ``our'' version of the file, with the contents + that we modified. +\item On the right is ``their'' version of the file, the one that + from the changeset that we're trying to merge with. +\end{itemize} +In the pane below these is the current \emph{result} of the merge. +Our task is to replace all of the red text, which indicates unresolved +conflicts, with some sensible merger of the ``ours'' and ``theirs'' +versions of the file. + +All four of these panes are \emph{locked together}; if we scroll +vertically or horizontally in any of them, the others are updated to +display the corresponding sections of their respective files. + +\begin{figure}[ht] + \centering + \grafix{kdiff3} + \caption{Using \command{kdiff3} to merge versions of a file} + \label{fig:tour-merge:kdiff3} +\end{figure} + +For each conflicting portion of the file, we can choose to resolve +the conflict using some combination of text from the base version, +ours, or theirs. We can also manually edit the merged file at any +time, in case we need to make further modifications. + +There are \emph{many} file merging tools available, too many to cover +here. They vary in which platforms they are available for, and in +their particular strengths and weaknesses. Most are tuned for merging +files containing plain text, while a few are aimed at specialised file +formats (generally XML). + +\subsection{A worked example} + +In this example, we will reproduce the file modification history of +figure~\ref{fig:tour-merge:conflict} above. Let's begin by creating a +repository with a base version of our document. +\interaction{tour-merge-conflict.wife} +We'll clone the repository and make a change to the file. +\interaction{tour-merge-conflict.cousin} +And another clone, to simulate someone else making a change to the +file. (This hints at the idea that it's not all that unusual to merge +with yourself when you isolate tasks in separate repositories, and +indeed to find and resolve conflicts while doing so.) +\interaction{tour-merge-conflict.son} +Having created two different versions of the file, we'll set up an +environment suitable for running our merge. +\interaction{tour-merge-conflict.pull} + +In this example, I won't use Mercurial's normal \command{hgmerge} +program to do the merge, because it would drop my nice automated +example-running tool into a graphical user interface. Instead, I'll +set \envar{HGMERGE} to tell Mercurial to use the non-interactive +\command{merge} command. This is bundled with many Unix-like systems. +If you're following this example on your computer, don't bother +setting \envar{HGMERGE}. +\interaction{tour-merge-conflict.merge} +Because \command{merge} can't resolve the conflicting changes, it +leaves \emph{merge markers} inside the file that has conflicts, +indicating which lines have conflicts, and whether they came from our +version of the file or theirs. + +Mercurial can tell from the way \command{merge} exits that it wasn't +able to merge successfully, so it tells us what commands we'll need to +run if we want to redo the merging operation. This could be useful +if, for example, we were running a graphical merge tool and quit +because we were confused or realised we had made a mistake. + +If automatic or manual merges fail, there's nothing to prevent us from +``fixing up'' the affected files ourselves, and committing the results +of our merge: +\interaction{tour-merge-conflict.commit} + +\section{Simplifying the pull-merge-commit sequence} +\label{sec:tour-merge:fetch} + +The process of merging changes as outlined above is straightforward, +but requires running three commands in sequence. +\begin{codesample2} + hg pull + hg merge + hg commit -m 'Merged remote changes' +\end{codesample2} +In the case of the final commit, you also need to enter a commit +message, which is almost always going to be a piece of uninteresting +``boilerplate'' text. + +It would be nice to reduce the number of steps needed, if this were +possible. Indeed, Mercurial is distributed with an extension called +\hgext{fetch} that does just this. + +Mercurial provides a flexible extension mechanism that lets people +extend its functionality, while keeping the core of Mercurial small +and easy to deal with. Some extensions add new commands that you can +use from the command line, while others work ``behind the scenes,'' +for example adding capabilities to the server. + +The \hgext{fetch} extension adds a new command called, not +surprisingly, \hgcmd{fetch}. This extension acts as a combination of +\hgcmd{pull}, \hgcmd{update} and \hgcmd{merge}. It begins by pulling +changes from another repository into the current repository. If it +finds that the changes added a new head to the repository, it begins a +merge, then commits the result of the merge with an +automatically-generated commit message. If no new heads were added, +it updates the working directory to the new tip changeset. + +Enabling the \hgext{fetch} extension is easy. Edit your +\sfilename{.hgrc}, and either go to the \rcsection{extensions} section +or create an \rcsection{extensions} section. Then add a line that +simply reads ``\Verb+fetch +''. +\begin{codesample2} + [extensions] + fetch = +\end{codesample2} +(Normally, on the right-hand side of the ``\texttt{=}'' would appear +the location of the extension, but since the \hgext{fetch} extension +is in the standard distribution, Mercurial knows where to search for +it.) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 7be02466421b -r b0db5adf11c1 ja/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-manual-merge.dot Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-manual.dot Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-non-tip.dot Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-simple.dot Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,4 @@ +digraph undo_simple { + "first change" -> "second change"; + "second change" -> "back out\nsecond change"; +} diff -r 7be02466421b -r b0db5adf11c1 ja/undo.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo.tex Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,763 @@ +\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. + +(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. + +\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 repoository. 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 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 7be02466421b -r b0db5adf11c1 ja/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-after-commit.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-branch.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-merge.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-pre-branch.svg Wed Feb 06 17:43:11 2008 +0900 @@ -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 7be02466421b -r b0db5adf11c1 ja/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir.svg Wed Feb 06 17:43:11 2008 +0900 @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + History in repository + + + + + e7639888bb2f + + + + 000000000000 + + First parent + Second parent + + Parents of working directory + + + +