# HG changeset patch # User Josef "Jeff" Sipek # Date 1153800144 14400 # Node ID fa8bafe467cb55a3a7a4b78972b3948470964c87 # Parent b8539d91c84d91e8beb2f4e954b77513ee8e5239# Parent 6f37e6a7d8cd7d387831b1d83a3c657f00f352b2 Merge with upstream diff -r b8539d91c84d -r fa8bafe467cb en/00book.tex --- a/en/00book.tex Mon Jul 24 23:57:52 2006 -0400 +++ b/en/00book.tex Tue Jul 25 00:02:24 2006 -0400 @@ -1,57 +1,58 @@ -% 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]{hyperref} - -\include{99defs} - -\title{Distributed revision control with Mercurial} -\author{Bryan O'Sullivan} -\date{Copyright \copyright\ 2006 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 revision \input{build_id}.} - -\makeindex - -\begin{document} - -\maketitle - -\addcontentsline{toc}{chapter}{Contents} -\pagenumbering{roman} -\tableofcontents -\listoffigures -%\listoftables - -\pagenumbering{arabic} - -\include{preface} -\include{intro} -\include{concepts} -\include{hook} -\include{mq} - -\appendix -\include{license} -\addcontentsline{toc}{chapter}{Bibliography} -\bibliographystyle{alpha} -\bibliography{99book} - -\printindex - -\end{document} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: t -%%% End: +% 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]{hyperref} + +\include{99defs} + +\title{Distributed revision control with Mercurial} +\author{Bryan O'Sullivan} +\date{Copyright \copyright\ 2006 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 revision \input{build_id}.} + +\makeindex + +\begin{document} + +\maketitle + +\addcontentsline{toc}{chapter}{Contents} +\pagenumbering{roman} +\tableofcontents +\listoffigures +%\listoftables + +\pagenumbering{arabic} + +\include{preface} +\include{intro} +\include{concepts} +\include{daily} +\include{hook} +\include{mq} + +\appendix +\include{license} +\addcontentsline{toc}{chapter}{Bibliography} +\bibliographystyle{alpha} +\bibliography{99book} + +\printindex + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r b8539d91c84d -r fa8bafe467cb en/99defs.tex --- a/en/99defs.tex Mon Jul 24 23:57:52 2006 -0400 +++ b/en/99defs.tex Tue Jul 25 00:02:24 2006 -0400 @@ -1,26 +1,80 @@ -\newcommand{\bug}[1]{\index{Mercurial issue!no.~#1}\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial issue no.~#1}} +% 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}} + +% 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}''} + +% Shell/system command. \newcommand{\command}[1]{\index{\texttt{#1} command}\texttt{#1}} + +% Shell/system command, with arguments. \newcommand{\cmdargs}[2]{\index{\texttt{#1} command}``\texttt{#1 #2}''} -\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''} + +% Mercurial command option. \newcommand{\hgopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}} + +% 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{#1.#2}} + +% hgrc file. \newcommand{\hgrc}{\index{\texttt{hgrc} file}\texttt{hgrc}} + +% 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}} + +% Note: blah blah. \newsavebox{\notebox} \newenvironment{note}% {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Note:}\space}% @@ -29,11 +83,18 @@ {\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.out}} +% Graphics inclusion. \ifpdf - \newcommand{\grafix}[1]{\includegraphics[scale=0.2]{#1_pdf.png}} + \newcommand{\grafix}[1]{\includegraphics{#1}} \else \newcommand{\grafix}[1]{\includegraphics{#1.png}} \fi diff -r b8539d91c84d -r fa8bafe467cb en/Makefile --- a/en/Makefile Mon Jul 24 23:57:52 2006 -0400 +++ b/en/Makefile Tue Jul 25 00:02:24 2006 -0400 @@ -1,121 +1,126 @@ -# This makefile requires GNU make. - -hg_id := $(shell hg parents --template '{node|short}\n' | head -1) - -sources := \ - 00book.tex \ - 99book.bib \ - 99defs.tex \ - build_id.tex \ - concepts.tex \ - hook.tex \ - intro.tex \ - mq.tex \ - preface.tex - -image-sources := \ - mq-stack.svg - -example-sources := \ - examples/run-example \ - examples/hook.simple \ - examples/concepts \ - examples/mq.qinit-help \ - examples/mq.diff \ - examples/mq.tarball \ - examples/mq.tools \ - examples/mq.tutorial - -latex-options = \ - -interaction batchmode \ - -output-directory $(dir $(1)) \ - -jobname $(basename $(notdir $(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-sources:%.svg=%.pdf) examples - $(call pdf) - -html: html/onepage/hgbook.html html/split/hgbook.html - -# 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, -# so instead I mangle the script itself. - -define htlatex - mkdir -p $(dir $(1)) - head -2 $(shell which htlatex) > $(dir $(1))/htlatex.book - cp 99book.bib $(dir $@) - echo '(cd $(dir $@) && bibtex $(basename $(notdir $@)))' >> $(dir $(1))/htlatex.book - echo '(cd $(dir $@) && makeindex $(basename $(notdir $@)))' >> $(dir $(1))/htlatex.book - head -3 $(shell which htlatex) >> $(dir $(1))/htlatex.book - echo 'echo status $$$$' >> $(dir $(1))/htlatex.book - chmod 755 $(dir $(1))/htlatex.book - TEXINPUTS=$(dir $(2)): $(dir $(1))/htlatex.book $(2) "xhtml,html4-uni,$(3)" " -cunihtf -utf8" "" "$(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))) - perl -pi -e 's/�([0-7][0-9a-f]);/chr(hex($$1))/egi' $(dir $(1))/*.html -endef - -html/onepage/hgbook.html: $(sources) $(image-sources:%.svg=%.png) examples - $(call htlatex,$@,$<) - cp $(image-sources:%.svg=%.png) $(dir $@) - -html/split/hgbook.html: $(sources) $(image-sources:%.svg=%.png) examples - $(call htlatex,$@,$<,2) - cp $(image-sources:%.svg=%.png) $(dir $@) - -beta: beta/pdf/hgbook.pdf beta/html/onepage/hgbook.html beta/html/split/hgbook.html - -beta/%.tex: %.tex - ./fblinks $(hg_id) $(dir $@) $< - -beta/pdf/hgbook.pdf: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.pdf) examples fblinks - $(call pdf) - -beta/html/onepage/hgbook.html: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.png) examples - $(call htlatex,$@,$<) - cp $(image-sources:%.svg=%.png) $(dir $@) - -beta/html/split/hgbook.html: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.png) examples - $(call htlatex,$@,$<,2) - cp $(image-sources:%.svg=%.png) $(dir $@) - -# Produce 90dpi PNGs for the web. - -%.png: %.svg - inkscape -D -e $@ $< - -# Produce eps & pdf for the pdf - -%.pdf: %.eps - epstopdf $< - -%.eps: %.svg - inkscape -E $@ $< - -examples: examples/.run - -examples/.run: $(example-sources) - cd examples && ./run-example - -build_id.tex: $(wildcard ../.hg/00changelog.[id]) - echo -n $(hg_id) > build_id.tex - -clean: - rm -rf beta html pdf *.eps *.pdf *.png *.aux *.dvi *.log *.out \ - examples/*.out examples/.run build_id.tex +# This makefile requires GNU make. + +hg_id := $(shell hg parents --template '{node|short}\n' | head -1) + +sources := \ + 00book.tex \ + 99book.bib \ + 99defs.tex \ + build_id.tex \ + concepts.tex \ + daily.tex \ + hook.tex \ + intro.tex \ + mq.tex \ + preface.tex + +image-sources := \ + mq-stack.svg + +example-sources := \ + examples/daily.files \ + examples/hook.simple \ + examples/hook.ws \ + examples/concepts \ + examples/mq.qinit-help \ + examples/mq.dodiff \ + examples/mq.tarball \ + examples/mq.tools \ + examples/mq.tutorial + +latex-options = \ + -interaction batchmode \ + -output-directory $(dir $(1)) \ + -jobname $(basename $(notdir $(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-sources:%.svg=%.pdf) examples + $(call pdf) + +html: html/onepage/hgbook.html html/split/hgbook.html + +# 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, +# so instead I mangle the script itself. + +define htlatex + mkdir -p $(dir $(1)) + head -2 $(shell which htlatex) > $(dir $(1))/htlatex.book + cp 99book.bib $(dir $@) + echo '(cd $(dir $@) && bibtex $(basename $(notdir $@)))' >> $(dir $(1))/htlatex.book + echo '(cd $(dir $@) && makeindex $(basename $(notdir $@)))' >> $(dir $(1))/htlatex.book + head -3 $(shell which htlatex) >> $(dir $(1))/htlatex.book + echo 'echo status $$$$' >> $(dir $(1))/htlatex.book + chmod 755 $(dir $(1))/htlatex.book + TEXINPUTS=$(dir $(2)): $(dir $(1))/htlatex.book $(2) "xhtml,html4-uni,$(3)" " -cunihtf -utf8" "" "$(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))) + perl -pi -e 's/�([0-7][0-9a-f]);/chr(hex($$1))/egi' $(dir $(1))/*.html +endef + +html/onepage/hgbook.html: $(sources) $(image-sources:%.svg=%.png) examples + $(call htlatex,$@,$<) + cp $(image-sources:%.svg=%.png) $(dir $@) + +html/split/hgbook.html: $(sources) $(image-sources:%.svg=%.png) examples + $(call htlatex,$@,$<,2) + cp $(image-sources:%.svg=%.png) $(dir $@) + +beta: beta/pdf/hgbook.pdf beta/html/onepage/hgbook.html beta/html/split/hgbook.html + +beta/%.tex: %.tex + ./fblinks $(hg_id) $(dir $@) $< + +beta/pdf/hgbook.pdf: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.pdf) examples fblinks + $(call pdf) + +beta/html/onepage/hgbook.html: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.png) examples + $(call htlatex,$@,$<) + cp $(image-sources:%.svg=%.png) $(dir $@) + +beta/html/split/hgbook.html: $(sources:%.tex=beta/%.tex) $(image-sources:%.svg=%.png) examples + $(call htlatex,$@,$<,2) + cp $(image-sources:%.svg=%.png) $(dir $@) + +# Produce 90dpi PNGs for the web. + +%.png: %.svg + inkscape -D -e $@ $< + +# Produce eps & pdf for the pdf + +%.pdf: %.eps + epstopdf $< + +%.eps: %.svg + inkscape -E $@ $< + +examples: examples/.run + +examples/.run: $(example-sources:%=%.run) + touch examples/.run + +examples/%.run: examples/% examples/run-example + cd examples && ./run-example $(notdir $<) + +build_id.tex: $(wildcard ../.hg/00changelog.[id]) + echo -n $(hg_id) > build_id.tex + +clean: + rm -rf beta html pdf *.eps *.pdf *.png *.aux *.dvi *.log *.out \ + examples/*.{out,run} examples/.run build_id.tex diff -r b8539d91c84d -r fa8bafe467cb en/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/daily.tex Tue Jul 25 00:02:24 2006 -0400 @@ -0,0 +1,129 @@ +\chapter{Mercurial in daily use} +\label{chap:daily} + +\section{Routine file management tasks} + +\subsection{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}''. + +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 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.) + +\begin{figure}[ht] + \interaction{daily.files.add} + \caption{Telling Mercurial to track a file} + \label{ex:daily:add} +\end{figure} + +Once you add a file, Mercurial will track every change you make to it +until you either remove or rename the file. + +\subsubsection{Aside: Mercurial tracks files, not directories} + +Mercurial does not track directory information. Instead, it tracks +the path to a file, and creates directories along a path when it needs +to. 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. + +\subsection{How to stop tracking a file} + +If 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}''. + +You might wonder why Mercurial requires you to explicitly tell it that +you are deleting a file. Earlier during the development of Mercurial, +you could simply delete a file however you pleased; Mercurial would +notice automatically when you next ran a \hgcmd{commit}, and stop +tracking the file. In practice, this made it too easy to accidentally +stop Mercurial from tracking a file. + +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}. +Other Mercurial commands will not do anything with missing files. + +If you have a missing file in your repository, you can run +\hgcmdargs{remove}{\hgopt{remove}{--after}} later on, to tell +Mercurial that you deleted the file. If you deleted the file by +accident, use \hgcmdargs{revert}{\emph{filename}} to restore the file +to its last committed state. + +\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. The +\hgcmd{commit} command also provides a \hgopt{commit}{-A} option that +performs an add-and-remove, immediately followed by a commit. This +lets you replace the following command sequence: +\begin{codesample2} + hg add + hg remove --after + hg commit +\end{codesample2} +with a single command, \hgcmdargs{commit}{\hgopt{commit}{-A}}. + +\subsection{Renaming files} + +To rename a file that is tracked by Mercurial, use the \hgcmd{rename} +command. This command behaves similarly to the Unix \command{mv} +command. If the last argument is a directory, it moves all prior +arguments into that directory. Otherwise, it renames a single file or +directory to the name given in the last argument. + +As with \hgcmd{remove}, you can tell Mercurial about a rename after +the fact using the \hgopt{remove}{--after} option. + +The na\"{i}ve way to ``rename'' a file is simply to rename the file +yourself, \hgcmd{remove} the old name, and \hgcmd{add} the new name. +However, if you do this, Mercurial will not know that there was any +relationship between the files in question, and it will not be able to +merge + +\subsection{Copying files} + +You can copy a file in two ways using mercurial. If you simply copy a +file and then \hgcmd{add} the new file, Mercurial will not know that +there was any relationship between the two files. However, if you + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r b8539d91c84d -r fa8bafe467cb en/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/daily.files Tue Jul 25 00:02:24 2006 -0400 @@ -0,0 +1,22 @@ +#!/bin/bash + +#$ name: add + +hg init a +cd a +echo content > filename +mkdir subdir +echo something > subdir/otherfile +hg status + +#$ name: hidden + +mkdir empty +touch empty/.hidden +hg add empty/.hidden +hg commit -m 'Manage an empty-looking directory' +ls empty +cd .. +hg clone a b +ls b +ls b/empty diff -r b8539d91c84d -r fa8bafe467cb en/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/data/check_whitespace.py Tue Jul 25 00:02:24 2006 -0400 @@ -0,0 +1,31 @@ +#!/usr/bin/python + +import os, re, sys + +count = 0 + +for line in os.popen('hg export tip'): + # remember the name of the file that this diff affects + m = re.match(r'^--- [^/]/([^\t])', line) + if m: + filename = m.group(1) + continue + # remember the line number + m = re.match(r'^@@ -(\d+),') + if m: + linenum = m.group(1) + continue + linenum += 1 + # check for an added line with trailing whitespace + m = re.match(r'^\+.*\s$', line) + if m: + print >> sys.stderr, ('%s:%d: trailing whitespace introduced' % + (filename, linenum)) + count += 1 + +if count: + # 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(count) diff -r b8539d91c84d -r fa8bafe467cb en/examples/hook.simple --- a/en/examples/hook.simple Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/hook.simple Tue Jul 25 00:02:24 2006 -0400 @@ -1,3 +1,5 @@ +#!/bin/bash + #$ name: init hg init hook-test diff -r b8539d91c84d -r fa8bafe467cb en/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/hook.ws Tue Jul 25 00:02:24 2006 -0400 @@ -0,0 +1,14 @@ +#!/bin/bash + +cp $EXAMPLE_DIR/data/check_whitespace.py . + +hg init a +cd a +echo '[hooks]' > .hg/hgrc +echo "pretxncommit.whitespace = hg export tip | (! grep -qP '^\\+.*[ \\t]$')" >> .hg/hgrc + +#$ name: simple + +cat .hg/hgrc +echo 'a ' > a +hg commit -A -m 'test with trailing whitespace' diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.diff --- a/en/examples/mq.diff Mon Jul 24 23:57:52 2006 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#$ name: diff - -echo 'this is my first line' > oldfile -echo 'my first line is here' > newfile - -diff -u oldfile newfile > tiny.patch - -cat tiny.patch - -patch < tiny.patch - -cat newfile diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/mq.dodiff Tue Jul 25 00:02:24 2006 -0400 @@ -0,0 +1,14 @@ +#!/bin/bash + +#$ name: diff + +echo 'this is my first line' > oldfile +echo 'my first line is here' > newfile + +diff -u oldfile newfile > tiny.patch + +cat tiny.patch + +patch < tiny.patch + +cat newfile diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.qinit-help --- a/en/examples/mq.qinit-help Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/mq.qinit-help Tue Jul 25 00:02:24 2006 -0400 @@ -1,3 +1,5 @@ +#!/bin/bash + echo '[extensions]' >> $HGRC echo 'hgext.mq =' >> $HGRC diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.tarball --- a/en/examples/mq.tarball Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/mq.tarball Tue Jul 25 00:02:24 2006 -0400 @@ -1,3 +1,5 @@ +#!/bin/bash + cp $EXAMPLE_DIR/data/netplug-*.tar.bz2 . ln -s /bin/true download diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.tools --- a/en/examples/mq.tools Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/mq.tools Tue Jul 25 00:02:24 2006 -0400 @@ -1,3 +1,5 @@ +#!/bin/bash + cp $EXAMPLE_DIR/data/remove-redundant-null-checks.patch . #$ name: tools diff -r b8539d91c84d -r fa8bafe467cb en/examples/mq.tutorial --- a/en/examples/mq.tutorial Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/mq.tutorial Tue Jul 25 00:02:24 2006 -0400 @@ -1,3 +1,5 @@ +#!/bin/bash + echo '[extensions]' >> $HGRC echo 'hgext.mq =' >> $HGRC diff -r b8539d91c84d -r fa8bafe467cb en/examples/run-example --- a/en/examples/run-example Mon Jul 24 23:57:52 2006 -0400 +++ b/en/examples/run-example Tue Jul 25 00:02:24 2006 -0400 @@ -10,6 +10,7 @@ import re import shutil import signal +import stat import sys import tempfile import time @@ -121,6 +122,7 @@ # then its output ofp.write(tex_escape(output)) self.status('\n') + open(self.name + '.run', 'w') finally: try: output = self.sendreceive('exit\n') @@ -142,8 +144,10 @@ for name in os.listdir(path): 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) - if os.path.isfile(pathname): + st = os.lstat(pathname) + if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: example(pathname).run() print >> open(os.path.join(path, '.run'), 'w'), time.asctime() diff -r b8539d91c84d -r fa8bafe467cb en/hook.tex --- a/en/hook.tex Mon Jul 24 23:57:52 2006 -0400 +++ b/en/hook.tex Tue Jul 25 00:02:24 2006 -0400 @@ -9,6 +9,207 @@ 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{Using hooks with shared access to a repository} + +If you want to use hooks to so some automated work in a repository +that a number of people have ahred 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 te 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 in which a +reader can see the metadata for changesets that are, strictly +speaking, not yet permanent. The longer the hook runs, the bigger the +window. + +A good use for the \hook{pretxnchangegroup} hook would be to +automatically build and test incoming changes before they are accepted +into the repository, so that you can 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; someone can pull untested changes. + +The safest answer to this challenge is to set up such a ``gatekeeper'' +repository as \emph{unidirectional}. It can take changes pushed in +from the outside, but nobody can 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. + \section{A short tutorial on using hooks} \label{sec:hook:simple} @@ -95,11 +296,18 @@ comment contains a bug ID. If it does, the commit can complete. If not, the commit is rolled back. -\section{Choosing how to write a hook} -\label{sec:hook:impl} +\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 called within the Mercurial +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 @@ -119,7 +327,7 @@ performance (probably the majority of hooks), a shell script is perfectly fine. -\section{Hook parameters} +\subsection{Hook parameters} \label{sec:hook:param} Mercurial calls each hook with a set of well-defined parameters. In @@ -128,10 +336,468 @@ environment variable. Whether your hook is written in Python or as a shell script, the -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'') - +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} Python, 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{Enforcing coding guidelines in your own repository} + +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 tend to 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. + +\section{Hook reference} +\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 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. If +it were to do so, it would probably cause calls to the Mercurial API, +or operations after the hook finishes, 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. + +\subsection{External hook execution} + +An external hook is passed to the user's shell for execution, so +features of that shell, such as variable substitution and command +redirection, are available. The hook is run in the root directory of +the repository. + +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 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{The \hook{changegroup} hook} +\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}. +\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{The \hook{commit} hook} +\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{The \hook{incoming} hook} +\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 othher +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. +\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{The \hook{outgoing} hook} +\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. + 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. +\end{itemize} + +See also: \hook{preoutgoing} (section~\ref{sec:hook:preoutgoing}) + +\subsection{The \hook{prechangegroup} hook} +\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 to ``freeze'' a server-hosted branch +temporarily or permanently. + +This hook is not passed any parameters. + +See also: \hook{changegroup} (section~\ref{sec:hook:changegroup}), +\hook{incoming} (section~\ref{sec:hook:incoming}), , +\hook{pretxnchangegroup} (section~\ref{sec:hook:pretxnchangegroup}) + +\subsection{The \hook{precommit} hook} +\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{The \hook{preoutgoing} hook} +\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 the + documentation for the \texttt{source} parameter to the + \hook{outgoing} hook, in section~\ref{sec:hook:outgoing}, for + possible values of this parameter.. +\end{itemize} + +See also: \hook{outgoing} (section~\ref{sec:hook:outgoing}) + +\subsection{The \hook{pretag} hook} +\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/tags}) 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{The \hook{pretxnchangegroup} hook} +\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 are the same as for the \hook{changegroup} +hook; see section~\ref{sec:hook:changegroup} for details. + +See also: \hook{changegroup} (section~\ref{sec:hook:changegroup}), +\hook{incoming} (section~\ref{sec:hook:incoming}), +\hook{prechangegroup} (section~\ref{sec:hook:prechangegroup}) + +\subsection{The \hook{pretxncommit} hook} +\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 are the same as for the \hook{commit} hook; +see section~\ref{sec:hook:commit} for details. + +See also: \hook{precommit} (section~\ref{sec:hook:precommit}) + +\subsection{The \hook{preupdate} hook} +\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{The \hook{tag} hook} +\label{sec:hook:tag} + +This hook is run after a tag has been created. + +Parameters to this hook are the same as for the \hook{pretag} hook; +see section~\ref{sec:hook:pretag} for details. + +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{The \hook{update} hook} +\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 diff -r b8539d91c84d -r fa8bafe467cb en/mq.tex --- a/en/mq.tex Mon Jul 24 23:57:52 2006 -0400 +++ b/en/mq.tex Tue Jul 25 00:02:24 2006 -0400 @@ -141,7 +141,7 @@ these commands in action. \begin{figure}[ht] - \interaction{mq.diff.diff} + \interaction{mq.dodiff.diff} \caption{Simple uses of the \command{diff} and \command{patch} commands} \label{ex:mq:diff} \end{figure} @@ -336,7 +336,7 @@ \begin{figure}[ht] \centering - \includegraphics{mq-stack} + \grafix{mq-stack} \caption{Applied and unapplied patches in the MQ patch stack} \label{fig:mq:stack} \end{figure}