# HG changeset patch # User Yoshiki Yazawa # Date 1240241800 -32400 # Node ID 019040fbf5f5ead2956524e5586a79f22186919b # Parent 5981a0f7540ae9d9d8cc481625984aeb34e3b420# Parent 29f0f79cf6146b747b2cbc9afe658f6b23ee6810 merged to upstream: phase 1 diff -r 5981a0f7540a -r 019040fbf5f5 .hgignore --- a/.hgignore Mon Apr 20 23:50:34 2009 +0900 +++ b/.hgignore Tue Apr 21 00:36:40 2009 +0900 @@ -1,37 +1,32 @@ -[^/]+/auto/ -[^/]+/dist/ -[^/]+/html/ +[^/]+/htdocs/ syntax: glob -beta/*.tex -build_id.tex -hg_id.tex -*.4[ct][ct] -*.aux -*.bbl -*.bib -*.blg -*.dvi -*.eps +*-tmp.* *.err -*.idx -*.ilg -*.ind -*.lg -*.lo[fgt] *.lxo +*.mo *.orig -*/pdf/*.out -*.pdf +*.out *.png -*.ps +*.pyc *.rej *.run -*.tmp -*.toc -*.xref *~ -.*.swp +.*.sw[op] .\#* .run +.validated-00book.xml +Makefile.vars +build +en/all-ids.dat +en/complete.xml +en/examples/results +en/html +en/svn +stylesheets/system-xsl +tools +web/hgbook/.database.sqlite3 +web/hgbook/secrets.py +web/index-read.html.in +xsl/system-xsl diff -r 5981a0f7540a -r 019040fbf5f5 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,243 @@ +# +# Makefile for the hgbook, top-level +# +include Makefile.vars + +FORMATS=html html-single pdf epub + +PO_LANGUAGES := zh +DBK_LANGUAGES := en +LANGUAGES := $(DBK_LANGUAGES) $(PO_LANGUAGES) + +UPDATEPO = PERLLIB=$(PO4A_LIB) $(PO4A_HOME)/po4a-updatepo -M UTF-8 \ + -f docbook -o doctype=docbook -o includeexternal \ + -o nodefault=" " \ + -o untranslated=" " +TRANSLATE = PERLLIB=$(PO4A_LIB) $(PO4A_HOME)/po4a-translate -M UTF-8 \ + -f docbook -o doctype=docbook -o includeexternal \ + -o nodefault=" " \ + -o untranslated=" " \ + -k 0 + +#rev_id = $(shell hg parents --template '{node|short} ({date|isodate})') +rev_id = $(shell hg parents --template '{node|short} ({date|shortdate})') + +images := \ + en/figs/feature-branches.png \ + en/figs/filelog.png \ + en/figs/metadata.png \ + en/figs/mq-stack.png \ + en/figs/revlog.png \ + en/figs/snapshot.png \ + en/figs/tour-history.png \ + en/figs/tour-merge-conflict.png \ + en/figs/tour-merge-merge.png \ + en/figs/tour-merge-pull.png \ + en/figs/tour-merge-sep-repos.png \ + en/figs/undo-manual-merge.png \ + en/figs/undo-manual.png \ + en/figs/undo-non-tip.png \ + en/figs/undo-simple.png \ + en/figs/wdir-after-commit.png \ + en/figs/wdir-branch.png \ + en/figs/wdir-merge.png \ + en/figs/wdir.png \ + en/figs/wdir-pre-branch.png + +help: + @echo " make epub [LINGUA=en|zh|...]" + @echo " make html [LINGUA=en|zh|...]" + @echo " make html-single [LINGUA=en|zh|...]" + @echo " make pdf [LINGUA=en|zh|...]" + @echo " make validate [LINGUA=en|zh|...] # always before commit!" + @echo " make tidypo [LINGUA=zh|...] # always before commit!" + @echo " make updatepo [LINGUA=zh|...] # update po files." + @echo " make all [LINGUA=en|zh|...]" + @echo " make stat # print statistics about po files." + @echo " make clean # Remove the build files." + +clean: + @rm -fr build po/*.mo hello en/hello en/html en/.validated-00book.xml en/examples/.run en/examples/results \ + stylesheets/system-xsl en/figs/*-tmp.svg \ + en/figs/feature-branches.png \ + en/figs/filelog.png \ + en/figs/feature-branches.png \ + en/figs/filelog.png \ + en/figs/metadata.png \ + en/figs/mq-stack.png \ + en/figs/revlog.png \ + en/figs/snapshot.png \ + en/figs/tour-history.png \ + en/figs/tour-merge-conflict.png \ + en/figs/tour-merge-merge.png \ + en/figs/tour-merge-pull.png \ + en/figs/tour-merge-sep-repos.png \ + en/figs/undo-manual-merge.png \ + en/figs/undo-manual.png \ + en/figs/undo-non-tip.png \ + en/figs/undo-simple.png \ + en/figs/wdir-after-commit.png \ + en/figs/wdir-branch.png \ + en/figs/wdir-merge.png \ + en/figs/wdir-pre-branch.png \ + en/figs/wdir.png + +all: +ifdef LINGUA + for f in $(FORMATS); do \ + $(MAKE) LINGUA=$(LINGUA) $$f; \ + done +else + for l in $(LANGUAGES); do \ + for f in $(FORMATS); do \ + $(MAKE) LINGUA=$$l $$f; \ + done; \ + done +endif + +stat: + @( \ + LANG=C; export LANG; cd po; \ + for f in *.po; do \ + printf "%s\t" $$f; \ + msgfmt --statistics -c $$f; \ + done; \ + ) + +tidypo: +ifdef LINGUA + msgcat --sort-by-file --width=80 po/$(LINGUA).po > po/$(LINGUA).tmp && \ + mv po/$(LINGUA).tmp po/$(LINGUA).po; +else + for po in $(wildcard po/*.po); do \ + msgcat --sort-by-file --width=80 $$po > $$po.tmp && mv $$po.tmp $$po; \ + done +endif + +ifndef LINGUA +updatepo: + for l in $(PO_LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +updatepo: +ifneq "$(findstring $(LINGUA),$(PO_LANGUAGES))" "" + (cd po; \ + $(UPDATEPO) -m ../en/00book.xml -p $(LINGUA).po; \ + ) + $(MAKE) tidypo LINGUA=$(LINGUA) +endif +endif + +ifndef LINGUA +validate: + for l in $(LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +validate: build/$(LINGUA)/source/hgbook.xml + xmllint --nonet --noout --postvalid --xinclude $< + +ifneq "$(findstring $(LINGUA),$(DBK_LANGUAGES))" "" +$(LINGUA)/examples/.run: + (cd $(LINGUA)/examples; ./run-example -v -a) + +build/$(LINGUA)/source/hgbook.xml: $(wildcard $(LINGUA)/*.xml) $(images) $(LINGUA)/examples/.run + mkdir -p build/$(LINGUA)/source/figs + cp $(LINGUA)/figs/*.png build/$(LINGUA)/source/figs + cp stylesheets/hgbook.css build/$(LINGUA)/source + (cd $(LINGUA); xmllint --nonet --noent --xinclude --postvalid --output ../$@.tmp 00book.xml) + cat $@.tmp | sed 's/\$$rev_id\$$/${rev_id}/' > $@ +else +en/examples/.run: + (cd en/examples; ./run-example -v -a) + +build/en/source/hgbook.xml: + ${MAKE} LINGUA=en $@ + +build/$(LINGUA)/source/hgbook.xml: $(wildcard en/*.xml) po/$(LINGUA).po $(images) + mkdir -p build/$(LINGUA)/source/figs + cp en/figs/*.png build/$(LINGUA)/source/figs + cp stylesheets/hgbook.css build/$(LINGUA)/source + $(TRANSLATE) -m en/00book.xml -p po/$(LINGUA).po -l en/hgbook.xml.$(LINGUA) + xmllint --nonet --noent --xinclude --postvalid --output $@.tmp en/hgbook.xml.$(LINGUA) + cat $@.tmp | sed 's/\$$rev_id\$$/${rev_id}/' > $@ + mv en/hgbook.xml.$(LINGUA) build/$(LINGUA)/source +endif + +endif + +ifndef LINGUA +epub: + for l in $(LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +epub: build/$(LINGUA)/epub/hgbook.epub + +build/$(LINGUA)/epub/hgbook.epub: build/$(LINGUA)/source/hgbook.xml + mkdir -p build/$(LINGUA)/epub + (cd build/$(LINGUA)/source; $(DB2EPUB) -c hgbook.css -v hgbook.xml; mv hgbook.epub ../epub) +endif + +ifndef LINGUA +html: + for l in $(LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +html: build/$(LINGUA)/html/index.html + +build/$(LINGUA)/html/index.html: build/$(LINGUA)/source/hgbook.xml stylesheets/html.xsl stylesheets/$(LINGUA)/html.xsl + mkdir -p build/$(LINGUA)/html/figs + cp en/figs/*.png build/$(LINGUA)/html/figs + cp stylesheets/hgbook.css build/$(LINGUA)/html + xsltproc --output build/$(LINGUA)/html/ \ + stylesheets/$(LINGUA)/html.xsl build/$(LINGUA)/source/hgbook.xml +endif + +ifndef LINGUA +html-single: + for l in $(LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +html-single: build/$(LINGUA)/html-single/hgbook.html + +build/$(LINGUA)/html-single/hgbook.html: build/$(LINGUA)/source/hgbook.xml stylesheets/html-single.xsl stylesheets/$(LINGUA)/html-single.xsl + mkdir -p build/$(LINGUA)/html-single/figs + cp en/figs/*.png build/$(LINGUA)/html-single/figs + cp stylesheets/hgbook.css build/$(LINGUA)/html-single + xsltproc --output build/$(LINGUA)/html-single/hgbook.html \ + stylesheets/$(LINGUA)/html-single.xsl build/$(LINGUA)/source/hgbook.xml +endif + +ifndef LINGUA +pdf: + for l in $(LANGUAGES); do \ + $(MAKE) $@ LINGUA=$$l; \ + done +else +pdf: build/$(LINGUA)/pdf/hgbook.pdf + +build/$(LINGUA)/pdf/hgbook.pdf: build/$(LINGUA)/source/hgbook.xml stylesheets/fo.xsl stylesheets/$(LINGUA)/fo.xsl + mkdir -p build/$(LINGUA)/pdf + java -classpath $(JAVA_LIB)/saxon65.jar:$(JAVA_LIB)/saxon65-dbxsl.jar:$(JAVA_LIB)/xml-commons-resolver-1.2.jar:$(JAVA_LIB) \ + com.icl.saxon.StyleSheet \ + -x org.apache.xml.resolver.tools.ResolvingXMLReader \ + -y org.apache.xml.resolver.tools.ResolvingXMLReader \ + -r org.apache.xml.resolver.tools.CatalogResolver \ + -o build/$(LINGUA)/source/hgbook.fo \ + build/$(LINGUA)/source/hgbook.xml \ + stylesheets/$(LINGUA)/fo.xsl \ + fop1.extensions=1 + + (cd build/$(LINGUA)/source && $(FOP_HOME)/fop.sh -c $(FOP_HOME)/conf/userconfig.xml hgbook.fo ../pdf/hgbook.pdf) +endif + +en/figs/%.png: en/figs/%.svg en/fixsvg + en/fixsvg $< + inkscape -D -d 120 -e $@ $<-tmp.svg + +en/figs/%.svg: en/figs/%.dot + dot -Tsvg -o $@ $< diff -r 5981a0f7540a -r 019040fbf5f5 Makefile.vars.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.vars.tmpl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,20 @@ +# +# Please create your Makefile.vars file from this template file. +# +# Please use absolute path, DO NOT use relative path ! +# + +# po4a (>= 0.36.1): Only for PO based Makefile ! +# po4A_HOME=/usr/bin +# PO4A_LIB=/usr/share/perl5 +PO4A_HOME=/home/dongsheng/var/svn/i18n-zh/trunk/lib/po4a +PO4A_LIB=$(PO4A_HOME)/lib + +# saxon65.jar, saxon65-dbxsl.jar, xml-commons-resolver-1.2.jar: Only for pdf format ! +JAVA_LIB=/home/dongsheng/var/svn/i18n-zh/trunk/lib/share/java + +# fop (>= 0.9.6): Only for pdf format ! +FOP_HOME=/home/dongsheng/var/svn/i18n-zh/trunk/lib/fop + +# docbook-xsl (>= 1.74.3): Only for ePub format ! +DB2EPUB=/home/dongsheng/var/svn/i18n-zh/trunk/lib/docbook/docbook-xsl/epub/bin/dbtoepub diff -r 5981a0f7540a -r 019040fbf5f5 README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,16 @@ +Mercurial: The Definitive Guide +------------------------------- + +Welcome to the source code for the book. You can clone the definitive +copy of the source tree using Mercurial as follows: + + hg clone http://hg.serpentine.com/mercurial/book + +Here's a top-level tour of interesting directories: + +en English-language content +es Spanish-language content +examples Miscellaneous example scripts +tools Old, largely unused conversion scripts +web Content and comment system for http://hgbook.red-bean.com/ +xsl XSLT scripts for generating HTML diff -r 5981a0f7540a -r 019040fbf5f5 README.BUILD --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.BUILD Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,99 @@ +HOW-TO: Compiling the Mercurial Book +====================================== + +This Mercurial Book is written in DocBook 4.5. + +The goal of this document is to give simple instructions to anyone who +wants to compile this book into a useful format, like HTML or PDF. It +should state *exactly* which tools to use, and how to invoke them, in +simplest terms. + +Table of Contents: + + I. PRIMER + II. COMPILING THE DOCS +III. HACKING ON THE DOCS + +I. PRIMER + + DocBook has a tortured, confusing history. Before you do anything, + take a look at Eric Raymond's excellent "DocBook Demystification HOWTO": + + http://tldp.org/HOWTO/DocBook-Demystification-HOWTO/ + + It's very short and clears up many things. + + +II. COMPILING THE DOCS + + +1. Install XML DTD and XSL stylesheets for DocBook + + % sudo apt-get install docbook-xml docbook-xsl + +2. Install libxml2-utils + + % sudo apt-get install libxml2-utils + +3. Install graph drawing tools + + % sudo apt-get install graphviz inkscape + +4. Install pdf support + + % sudo apt-get install openjdk-6-jdk docbook-xsl-saxon libsaxon-java fop + + The Makefile will actually invoke tools/fop/fop.sh, you should do + some trick, let fop's CLASSPATH include saxon.jar and docbook-xsl-saxon.jar . + +5. Make + Run 'make' for more details, for example: + + * make all document(pdf, html and html-single for all languages) + % make all + + * make english document(pdf, html and html-single for all languages) + % make LINGUA=en all + + * make Chinese document(pdf, html and html-single for all languages) + % make LINGUA=zh all + + * make Chinese pdf document + % make LINGUA=zh pdf + +III. HACKING ON THE DOCS + +In addition to everything in section II: + + +1. Get a nice editing environment for SGML/XML. + + This isn't strictly required, but it's nice when your editor + colorizes things, understands the DTD, tells you what tags you can + insert, etc. + + If you use emacs, we recommend the PSGML major-mode. Most free + operating systems package it, or its home page is here: + + http://www.lysator.liu.se/projects/about_psgml.html + + If you use vim, you might check out xmledit, at: + + http://www.vim.org/scripts/script.php?script_id=301 + + +2. Get a validating parser. + + Actually, if you have what you need to compile the documentation, + then you almost certainly have an XML validator installed already - + it is called xmllint, and comes as part of libxml2. + + The makefile is preconfigured with a suitable invocation of it, + so simply run: + + $ make validate + +3. Read about DocBook. + + You'll want to get real intimate with a DocBook reference, such as + can be found at: http://www.docbook.org/tdg/en/html/ diff -r 5981a0f7540a -r 019040fbf5f5 contrib/hg-interdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hg-interdiff Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# +# Adapter for using interdiff with mercurial's extdiff extension. +# +# Copyright 2006 Bryan O'Sullivan +# +# This software may be used and distributed according to the terms of +# the GNU General Public License, incorporated herein by reference. + +import os, sys + +def walk(base): + # yield all non-directories below the base path. + for root, dirs, files in os.walk(base): + for f in files: + path = os.path.join(root, f) + yield path[len(base)+1:], path + else: + if os.path.isfile(base): + yield '', base + +# create list of unique file names under both directories. +files = dict(walk(sys.argv[1])) +files.update(walk(sys.argv[2])) +files = files.keys() +files.sort() + +def name(base, f): + if f: + path = os.path.join(base, f) + else: + path = base + # interdiff requires two files; use /dev/null if one is missing. + if os.path.exists(path): + return path + return '/dev/null' + +ret = 0 + +for f in files: + if os.system('interdiff "%s" "%s"' % (name(sys.argv[1], f), + name(sys.argv[2], f))): + ret = 1 + +sys.exit(ret) diff -r 5981a0f7540a -r 019040fbf5f5 contrib/hg-package --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hg-package Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,80 @@ +#!/bin/sh + +build_dir=`dirname "$0"`/../build +rev_id=`hg parents --template '{date|shortdate}' | sed 's/-//g'` + +for l in en zh; do + ( + if [ ! -d "${build_dir}/${l}" ] ; then + continue + fi + + cd ${build_dir}/${l}; + + f='html' + if [ -f "${f}/index.html" ] ; then + d=hgbook-${l}-${f} + rm -fr ${d} && cp -r ${f} ${d} && tar czf ../${d}-${rev_id}.tar.gz ${d} + fi + + f='html-single' + if [ -f "${f}/hgbook.html" ] ; then + d=hgbook-${l}-${f} + rm -fr ${d} && cp -r ${f} ${d} && tar czf ../${d}-${rev_id}.tar.gz ${d} + fi + + if [ -f "pdf/hgbook.pdf" ] ; then + cp pdf/hgbook.pdf ../hgbook-${l}-${rev_id}.pdf + gzip -f9 ../hgbook-${l}-${rev_id}.pdf + fi + + if [ -f "epub/hgbook.epub" ] ; then + cp epub/hgbook.epub ../hgbook-${l}-${rev_id}.epub + fi + ) +done + +upload_pass=$1 +upload_user=$2 + +# echo "upload_pass: ${upload_pass}" +# echo "upload_user: ${upload_user}" + +if [ "${upload_user}x" == "x" ]; then + upload_user="dongsheng.song" +fi + +if [ "${upload_pass}x" != "x" ]; then + ( + cd ${build_dir} + curl -s -O http://support.googlecode.com/svn/trunk/scripts/googlecode_upload.py + if [[ "0" != $? ]]; then + exit 1 + fi + + for l in en zh; do + if [ -f "hgbook-${l}-${rev_id}.epub" ] ; then + python googlecode_upload.py --user="${upload_user}" --password="${upload_pass}" \ + -p "i18n-zh" -l "Type-Docs,book,hgbook,hg,mercurial,ebook" \ + -s "Distributed revision control with Mercurial - ${l} - ePub" \ + hgbook-${l}-${rev_id}.epub + fi + + if [ -f "hgbook-${l}-${rev_id}.pdf.gz" ] ; then + python googlecode_upload.py --user="${upload_user}" --password="${upload_pass}" \ + -p "i18n-zh" -l "Type-Docs,book,hgbook,hg,mercurial" \ + -s "Distributed revision control with Mercurial - ${l} - pdf" \ + hgbook-${l}-${rev_id}.pdf.gz + fi + + for f in html html-single; do + if [ -f "hgbook-${l}-${f}-${rev_id}.tar.gz" ] ; then + python googlecode_upload.py --user="${upload_user}" --password="${upload_pass}" \ + -p "i18n-zh" -l "Type-Docs,book,hgbook,hg,mercurial" \ + -s "Distributed revision control with Mercurial - ${l} - ${f}" \ + hgbook-${l}-${f}-${rev_id}.tar.gz + fi + done + done + ) +fi diff -r 5981a0f7540a -r 019040fbf5f5 contrib/hg-replay --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hg-replay Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# Adapter for using interdiff with mercurial's extdiff extension. +# +# Copyright 2006 Bryan O'Sullivan +# +# This software may be used and distributed according to the terms of +# the GNU General Public License, incorporated herein by reference. + +import os +import shutil +import sys +import tempfile + +if len(sys.argv) < 4: + print >> sys.stderr, ('usage: %s srcrepo destrepo cset-to-omit [...]' % + os.path.basename(sys.argv[0])) + sys.exit(1) + +srcrepo, destrepo = sys.argv[1], sys.argv[2] +omit = sys.argv[3:] + +changemap = {} +revs = [] + +parent = None + +sys.stdout.write('gathering history...') +sys.stdout.flush() + +for line in os.popen("hg --cwd %r log -r0:tip --template '{rev}:{node} {parents}\n'" % srcrepo): + changes = line.split() + cset = changes[0].split(':')[1] + rev = len(revs) + changemap[cset] = rev + if len(changes) >= 2: + p1 = int(changes[1].split(':', 1)[0]) + if len(changes) == 3: + p2 = int(changes[2].split(':', 1)[0]) + else: + p2 = None + if len(changes) == 1: + p1 = parent + revs.append((cset, p1, p2)) + parent = rev + +sys.stdout.write(' %d revs\n' % len(revs)) + +def findrev(r): + try: + i = int(r) + if str(i) == r: + rev = i + if rev < 0: + rev += len(revs) + if rev < 0 or rev > len(revs): + print >> sys.stderr, 'bad changeset: %r' % r + sys.exit(1) + cset = revs[rev][0] + except ValueError: + cset = r + matches = [changemap[c] for c in changemap if c.startswith(cset)] + if len(matches) != 1: + print >> sys.stderr, 'bad changeset: %r' % r + sys.exit(1) + rev = matches[0] + return rev + +def run(cmd): + print cmd + ret = os.system(cmd) + if ret: + print >> sys.stderr, 'failure:', cmd + sys.exit(1) + +omit = map(findrev, omit) +omit.sort() +newrevs = revs[:omit[0]] +tip = len(newrevs) - 1 +run('hg clone -q -r%s %r %r' % (tip, srcrepo, destrepo)) + +os.environ['HGMERGE'] = 'true' + +patchdir = tempfile.mkdtemp(prefix='replay.') +try: + run('hg --cwd %r export --git -o %r%s%%R %d:tip' % + (srcrepo, patchdir, os.sep, omit[0]+1)) + for rev in xrange(omit[0], len(revs)): + if rev in omit: + print 'omit', rev + newrevs.append((None, revs[rev][1], None)) + continue + _, p1, p2 = revs[rev] + np1 = newrevs[p1][1] + if tip != np1: + run('hg --cwd %r update -q -C %s' % (destrepo, np1)) + np2 = None + if p2: + np2 = newrevs[p2][1] + run('hg --cwd %r merge -q %s' % (destrepo, np2)) + print >> sys.stderr, 'XXX - cannot handle merges properly yet' + run('hg --cwd %r import -q -f %r%s%d' % (destrepo, patchdir, os.sep, rev)) + tip = len(newrevs) - 1 + newrevs.append((None, tip, np2)) +finally: + print 'cleaning up ...' + #shutil.rmtree(patchdir) diff -r 5981a0f7540a -r 019040fbf5f5 contrib/latex-to-docbook --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/latex-to-docbook Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,198 @@ +#!/usr/bin/python +# +# This is the most horrible of hacks. Pretend you're not looking. + +import cStringIO as StringIO +import re, sys + +sections = { + 'chapter': 'chapter', + 'section': 'sect1', + 'subsection': 'sect2', + 'subsubsection': 'sect3', + } + +envs = { + 'codesample2': 'programlisting', + 'codesample4': 'programlisting', + 'enumerate': 'orderedlist', + 'figure': 'informalfigure', + 'itemize': 'itemizedlist', + 'note': 'note', + 'quote': 'blockquote', + } + +def process(ifp, ofp): + print >> ofp, '\n' + stack = [] + para = True + inlist = 0 + for line in ifp: + if line.startswith('%%% Local Variables:'): + break + line = (line.rstrip() + .replace('~', ' ') + .replace('&', '&') + .replace('---', '&emdash;') + .replace('\_', '_') + .replace('\{', '{') + .replace('\}', '}') + .replace('\$', '$') + .replace('\%', '%') + .replace('\#', '#') + .replace('<', '<') + .replace('>', '>') + .replace('``', '') + .replace("''", '') + .replace('\\', '\\')) + line = re.sub(r'\s*\\(?:centering|small)\b\s*', '', line) + line = re.sub(r'\\(?:hgrc\\|hgrc)\b', + r' /.hgrc', line) + line = re.sub(r'\\item\[(?P[^]]+)\]', r'\item \g:', line) + line = re.sub(r'\\bug{(?P\d+)}', + r'issue \g', line) + line = re.sub(r'\\cite{([^}]+)}', r'\1', line) + line = re.sub(r'\\hggopt{(?P[^}]+)}', + r'', line) + line = re.sub(r'\\hgxopt{(?P[^}]+)}{(?P[^}]+)}{(?P[^}]+)}', + r'', line) + line = re.sub(r'\\hgxcmd{(?P[^}]+)}{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\hgext{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\hgopt{(?P[^}]+)}{(?P[^}]+)}', + r'', + line) + line = re.sub(r'\\cmdopt{(?P[^}]+)}{(?P[^}]+)}', + r'', + line) + line = re.sub(r'\\hgcmd{(?P[^}]+)}', + r'hg \g', line) + line = re.sub(r'\\caption{(?P[^}]+?)}', + r'\g', line) + line = re.sub(r'\\grafix{(?P[^}]+)}', + r'XXX add text', line) + line = re.sub(r'\\envar{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\rcsection{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\rcitem{(?P[^}]+)}{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\dirname{(?P[^}]+?)}', + r'\g', line) + line = re.sub(r'\\filename{(?P[^}]+?)}', + r'\g', line) + line = re.sub(r'\\tildefile{(?P[^}]+)}', + r'~/\g', line) + line = re.sub(r'\\sfilename{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\sdirname{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\interaction{(?P[^}]+)}', + r'', line) + line = re.sub(r'\\excode{(?P[^}]+)}', + r'', line) + line = re.sub(r'\\pymod{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\pymodclass{(?P[^}]+)}{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\url{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\href{(?P[^}]+)}{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\command{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\option{(?P[^}]+)}', + r'', line) + line = re.sub(r'\\ref{(?P[^}]+)}', r'', line) + line = re.sub(r'\\emph{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\texttt{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\textbf{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\hook{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\tplfilter{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\tplkword{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\tplkwfilt{(?P[^}]+)}{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\[vV]erb(.)(?P[^\1]+?)\1', + r'\g', line) + line = re.sub(r'\\package{(?P[^}]+)}', + r'\g', line) + line = re.sub(r'\\hgcmdargs{(?P[^}]+)}{(?P[^}]+)}', + r'hg \g \g', + line) + line = re.sub(r'\\cmdargs{(?P[^}]+)}{(?P[^}]+)}', + r'\g \g', + line) + m = re.match(r'\\(chapter|section|subsection|subsubsection){(.*)}', line) + if m: + kind, content = m.groups() + sec = sections[kind] + while stack and stack[-1] >= sec: + close = stack.pop() + print >> ofp, '' % close + stack.append(sec) + print >> ofp, '<%s>\n%s' % (sec, content) + else: + m = re.match(r'\s*\\(begin|end){(?P[^}]+)}', line) + if m: + if not para: + print >> ofp, '' + if inlist: + ofp.write('') + para = True + state, env = m.groups() + env = envs[env] + if state == 'begin': + ofp.write('<') + if env in ('itemizedlist', 'orderedlist'): + inlist = 1 + else: + ofp.write('> ofp, env + '>' + else: + if line.startswith('\\item '): + if inlist > 1: + print >> ofp, '' + print >> ofp, '' + else: + inlist = 2 + para = True + line = line[6:] + if line and para: + if inlist: + ofp.write('') + ofp.write('') + para = False + if not line and not para: + print >> ofp, '' + if inlist: + ofp.write('') + para = True + print >> ofp, line + while stack: + print >> ofp, '' % stack.pop() + ofp.write('\n'.join(['\n'])) + + +if __name__ == '__main__': + for name in sys.argv[1:]: + if not name.endswith('.tex'): + continue + newname = name[:-3] + 'xml' + ofp = StringIO.StringIO() + process(open(name), ofp) + s = ofp.getvalue() + s = re.sub('\n+', '', s, re.M) + open(newname, 'w').write(s) diff -r 5981a0f7540a -r 019040fbf5f5 contrib/sillybench.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/sillybench.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,177 @@ +#!/usr/bin/python +# +# Silly benchmarking program, to give a vague idea of how fast a few +# tools are on a handful of common operations. +# +# Use a fairly big and real source tarball to test with: Firefox +# 2.0.0.3 (37622 files, 5374 directories, 343MB unpacked onto +# 4KB-blocksize ext3). + +import csv +import os +import shutil +import sys +import tempfile +import time +import urllib2 + +url = 'ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/2.0.0.3/source/firefox-2.0.0.3-source.tar.bz2' + +class CommandFailure(Exception): + pass + +class rcs(object): + def __init__(self): + self.logfp = open(self.__class__.__name__ + '.csv', 'w') + self.csv = csv.writer(self.logfp) + + def download(self): + name = url[url.rfind('/')+1:] + path = os.path.join(os.environ['HOME'], name) + if not os.path.isfile(path): + ofp = open(path + '.part', 'wb') + try: + ifp = urllib2.urlopen(url) + nbytes = ifp.info()['content-length'] + sys.stdout.write('%s: %s bytes ' % (name, nbytes)) + sys.stdout.flush() + while True: + data = ifp.read(131072) + if not data: break + sys.stdout.write('.') + sys.stdout.flush() + ofp.write(data) + del ofp + os.rename(path + '.part', path) + except: + if os.path.exists(path + '.part'): + os.unlink(path + '.part') + if os.path.exists(path): + os.unlink(path) + raise + return path + + def run(self, args, mustsucceed=True): + ret = os.spawnvp(os.P_WAIT, args[0], args) + if ret < 0: + msg = 'killed by signal %d' % (-ret) + if ret > 0: + msg = 'exited with status %d' % (ret) + if ret: + if mustsucceed: + raise CommandFailure('%s: %s' % (msg, ' '.join(args))) + print >> sys.stderr, 'WARNING: %s: %s' % (msg, ' '.join(args)) + + def time(self, *args, **kwargs): + start = time.time() + self.run(*args, **kwargs) + end = time.time() + return end - start + + def logtime(self, name, elapsed, rest=[]): + self.log('time:' + name, '%.3f' % elapsed, rest) + + def log(self, name, value, rest=[]): + item = (name, value, repr(rest)) + print ' '.join(item) + self.csv.writerow(item) + self.logfp.flush() + + def unpack(self): + tarball = self.download() + t = self.time(['tar', '-C', self.wdir, '-jxf', tarball]) + self.logtime('internal:untar', t) + for name in os.listdir(os.path.join(self.wdir, 'mozilla')): + os.rename(os.path.join(self.wdir, 'mozilla', name), + os.path.join(self.wdir, name)) + + def cleanup(self): + pass + + def add(self, paths): + pass + + def commit(self, msg, paths): + pass + + def status(self, path): + pass + + def remove(self, path): + pass + + +class subversion(rcs): + def __init__(self, root): + rcs.__init__(self) + self.repo = os.path.join(root, 'repo') + self.wdir = os.path.join(root, 'wc') + create = self.time(['svnadmin', 'create', '--fs-type=fsfs', self.repo]) + self.logtime('svn:create', create) + co = self.time(['svn', 'co', 'file://' + self.repo, self.wdir]) + self.logtime('svn:co', co) + self.logtime('init', create + co) + os.chdir(self.wdir) + + def dropmeta(self, names): + return [n for n in names if os.path.basename(n) != '.svn'] + + def add(self, paths): + t = self.time(['svn', 'add', '-q'] + paths) + self.logtime('add %r' % paths, t) + + def commit(self, msg, paths=[]): + if paths: + t = self.time(['svn', 'ci', '-q', '-m', msg] + paths) + else: + t = self.time(['svn', 'ci', '-q', '-m', msg]) + self.logtime('commit %r' % paths, t) + + +class mercurial(rcs): + def __init__(self, root): + rcs.__init__(self) + self.repo = os.path.join(root, 'repo') + self.wdir = self.repo + init = self.time(['hg', 'init', self.repo]) + self.logtime('init', init) + os.chdir(self.wdir) + + def dropmeta(self, names): + return [n for n in names if os.path.basename(n) != '.hg'] + + def add(self, paths): + t = self.time(['hg', 'add', '-q'] + paths) + self.logtime('add %r' % paths, t) + + def commit(self, msg, paths=[]): + if paths: + t = self.time(['hg', 'ci', '-q', '-m', msg] + paths) + else: + t = self.time(['hg', 'ci', '-q', '-m', msg]) + self.logtime('commit %r' % paths, t) + +def benchmark(cls): + oldcwd = os.getcwd() + root = tempfile.mkdtemp(prefix='sillybench.') + try: + print 'root', root + inst = cls(root) + inst.unpack() + names = inst.dropmeta(os.listdir('.')) + dirs = [n for n in names if os.path.isdir(n)] + nondirs = [n for n in names if not os.path.isdir(n)] + dirs.sort(key=hash) + names.sort(key=hash) + for d in dirs[:len(dirs)/2]: + inst.add([d]) + inst.commit('Add %r' % d, [d]) + inst.add(dirs[len(dirs)/2:] + names) + inst.commit('Add remaining dirs and files') + finally: + print >> sys.stderr, '[cleaning up...]' + shutil.rmtree(root) + os.chdir(oldcwd) + +benchmark(mercurial) +#benchmark(subversion) diff -r 5981a0f7540a -r 019040fbf5f5 en/00book.tex --- a/en/00book.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -% The use of oneside here is a temporary hack; \marginpar entries -% don't show up on odd pages of PDF output without it. Sigh. -\documentclass[oneside]{book} -\usepackage{enumerate} -\usepackage{fullpage} -\usepackage{makeidx} -\usepackage{ifpdf} -\usepackage{graphicx} -\usepackage{pslatex} -\usepackage{fancyvrb} -% 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 5981a0f7540a -r 019040fbf5f5 en/00book.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/00book.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%SHORTCUTS; + + + + +%AUTOSNIPPETS; +]> + + + Mercurial: The Definitive Guide + + + Compiled from $rev_id$ + + 1 + 9780596800673 + + + Bryan + O'Sullivan + + + + + Mike + Loukides + + + + 2006 + 2007 + 2008 + 2009 + Bryan O'Sullivan + + + + + &ch00; + + &ch01; + + &ch02; + + &ch03; + + &ch04; + + &ch05; + + &ch06; + + &ch07; + + &ch08; + + &ch09; + + &ch10; + + &ch11; + + &ch12; + + &ch13; + + &appB; + + &appC; + + &appD; + diff -r 5981a0f7540a -r 019040fbf5f5 en/99book.bib --- a/en/99book.bib Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -@Unpublished{gruenbacher:2005, - author = {Andreas Gruenbacher}, - title = {How To Survive With Many Patches (Introduction to \texttt{quilt})}, - year = {2005}, - month = {June}, - note = {\url{http://www.suse.de/~agruen/quilt.pdf}}, -} - -@InProceedings{web:europython, - author = {Bryan O'Sullivan}, - title = {Achieving High Performance in Mercurial}, - booktitle = {EuroPython Conference}, - year = {2006}, - month = {July}, - note = {\url{XXX}}, -} - -@Misc{web:diffstat, - author = {Thomas Dickey}, - title = {\texttt{diffstat}--make a histogram of \texttt{diff} output}, - note = {\url{http://dickey.his.com/diffstat/diffstat.html}}, -} - -@Misc{web:quilt, - author = {Andreas Gruenbacher, Martin Quinson, Jean Delvare}, - title = {Patchwork Quilt}, - note = {\url{http://savannah.nongnu.org/projects/quilt}}, -} - -@Misc{web:patchutils, - author = {Tim Waugh}, - title = {\texttt{patchutils}--programs that operate on patch files}, - note = {\url{http://cyberelk.net/tim/patchutils/}}, -} - -@Misc{web:mpatch, - author = {Chris Mason}, - title = {\texttt{mpatch}--help solve patch rejects}, - note = {\url{http://oss.oracle.com/~mason/mpatch/}}, -} - -@Misc{web:wiggle, - author = {Neil Brown}, - title = {\texttt{wiggle}--apply conflicting patches}, - note = {\url{http://cgi.cse.unsw.edu.au/~neilb/source/wiggle/}}, -} - -@Misc{web:mysql-python, - author = {Andy Dustman}, - title = {MySQL for Python}, - note = {\url{http://sourceforge.net/projects/mysql-python}}, -} - -@Misc{web:changelog, - author = {Richard Stallman, GNU Project volunteers}, - title = {GNU Coding Standards---Change Logs}, - note = {\url{http://www.gnu.org/prep/standards/html_node/Change-Logs.html}}, -} - -@Misc{web:macpython, - author = {Bob Ippolito, Ronald Oussoren}, - title = {Universal MacPython}, - note = {\url{http://bob.pythonmac.org/archives/2006/04/10/python-and-universal-binaries-on-mac-os-x/}}, -} - -@Misc{web:putty, - author = {Simon Tatham}, - title = {PuTTY---open source ssh client for Windows}, - note = {\url{http://www.chiark.greenend.org.uk/~sgtatham/putty/}}, -} - -@Misc{web:configparser, - author = {Python.org}, - title = {\texttt{ConfigParser}---Configuration file parser}, - note = {\url{http://docs.python.org/lib/module-ConfigParser.html}}, -} diff -r 5981a0f7540a -r 019040fbf5f5 en/99defs.tex --- a/en/99defs.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -% Bug ID. -%\newcommand{\bug}[1]{\index{Mercurial bug database! -%\href{http://www.selenic.com/mercurial/bts/issue#1}{bug ~#1}}\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial bug no.~#1}} - -\newcommand{\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: yatex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/Makefile --- a/en/Makefile Mon Apr 20 23:50:34 2009 +0900 +++ b/en/Makefile Tue Apr 21 00:36:40 2009 +0900 @@ -1,64 +1,26 @@ # 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 := $(wildcard figs/*.dot figs/*.gif figs/*.png figs/*.svg) -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 +xml-src-files := \ + 00book.xml \ + app*.xml \ + ch*.xml image-dot := $(filter %.dot,$(image-sources)) image-svg := $(filter %.svg,$(image-sources)) -image-png := $(filter %.png,$(image-sources)) +image-oth := $(filter %.gif %.png,$(image-sources)) + +obj-web := html +obj-websup := $(obj-web)/support +obj-web-read := $(obj-web)/read -image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) -image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) -#image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png) -image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png:%.png=%.eps) +image-web := \ + $(image-dot:%.dot=$(obj-web-read)/%.png) \ + $(image-svg:%.svg=$(obj-web-read)/%.png) \ + $(image-oth:%=$(obj-web-read)/%) -example-sources := \ +example-sources-by-name := \ backout \ bisect \ branching \ @@ -90,6 +52,49 @@ tour \ tour-merge-conflict +example-sources := \ + $(example-sources-by-name:%=examples/%) \ + $(wildcard examples/ch*/*) + +extras-web-base := \ + $(obj-web)/index.html \ + $(obj-web)/robots.txt \ + $(obj-websup)/form-min.js \ + $(obj-websup)/form.js \ + $(obj-websup)/hsbook.js \ + $(obj-websup)/jquery-min.js \ + $(obj-websup)/jquery.js \ + $(obj-websup)/styles.css + +extras-web := $(extras-web-base) $(extras-web-base:%=%.gz) + +xsltproc := xsltproc +xsltproc-opts := --nonet --xinclude --path '$(xml-path)' + +xmllint := xmllint +xmllint-opts := --noout --nonet --valid + +system-xsl-dir := $(firstword $(wildcard \ + /usr/share/sgml/docbook/xsl-stylesheets \ + /usr/share/xml/docbook/stylesheet/nwalsh \ + )) + +# Bletcherousness. + +ifneq ($(wildcard /usr/share/sgml/docbook/xml-dtd-4.4-*),) +dtd-dir := $(wildcard /usr/share/sgml/docbook/xml-dtd-4.4-*) +else +ifneq ($(wildcard /usr/share/xml/docbook/schema/dtd/4.4),) +dtd-dir := $(wildcard /usr/share/xml/docbook/schema/dtd/4.4) +else +$(error Do not know where to look for DocBook XML 4.4 DTD) +endif +endif + +ifeq ($(system-xsl-dir),) +$(error add a suitable directory to system-xsl-dir) +endif + example-prereqs := \ /usr/bin/merge @@ -98,11 +103,6 @@ ../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') @@ -110,132 +110,111 @@ hg-version = $(shell hg version -q | \ sed 's,.*(version \(unknown\|[a-f0-9+]*\)),\1,') -all: dvi - -#dvi: $(sources) $(image-eps) examples -dvi: $(sources) $(image-eps) - platex 00book.tex +all: web complete.xml - cp 00book.aux hgbook.aux - bibtex hgbook +../stylesheets/system-xsl: $(system-xsl-dir) + ln -s $< $@ - platex 00book.tex - platex 00book.tex - platex 00book.tex +web: ../stylesheets/system-xsl websup html - - - +html: $(obj-web-read)/index.html - -pdf: pdf/hgbook.pdf - -define pdf - mkdir -p $(dir $@) - TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) +../web/index-read.html.in: ../web/genindex.py $(xml-src-files) + cd ../web && ./genindex.py - 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 +$(obj-web-read)/index.html: ../stylesheets/system-xsl .validated-00book.xml ../web/index-read.html.in + xsltproc $(xsltproc-opts) -o $(obj-web-read)/x ../stylesheets/chunk-stylesheet.xsl 00book.xml + python ../web/texpand.py ../web/index-read.html.in html/read/index.html + for i in $(obj-web-read)/*.html; do \ + gzip -9 -c $$i > $$i.gz; \ + done -#pdf/hgbook.pdf: $(sources) $(image-pdf) examples -pdf/hgbook.pdf: $(sources) $(image-pdf) - $(call pdf) - -html: onepage split - -onepage: $(htlatex) html/onepage/hgbook.html html/onepage/hgbook.css $(image-html:%=html/onepage/%) +websup: $(extras-web) $(image-web) + mkdir -p $(obj-websup)/figs $(obj-web-read)/figs + cp ../stylesheets/system-xsl/images/*.png $(obj-websup)/figs + cp -f ../web/icons/*.png $(obj-websup)/figs -html/onepage/%: % - cp $< $@ - -split: $(htlatex) html/split/hgbook.html html/split/hgbook.css $(image-html:%=html/split/%) - -html/split/%: % - cp $< $@ +complete.xml: .validated-00book.xml + $(xsltproc) $(xsltproc-opts) -o $@ ../stylesheets/dtd-profile.xsl 00book.xml -# 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. +all-ids.dat: ../stylesheets/all-ids.xsl $(xml-src-files) + $(xsltproc) $(xsltproc-opts) -o $@ ../stylesheets/all-ids.xsl 00book.xml + +web: websup -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 +valid: .validated-00book.xml -#html/onepage/hgbook.html: $(sources) $(image-html) examples bookhtml.cfg -html/onepage/hgbook.html: $(sources) $(image-html) bookhtml.cfg - $(call htlatex,$@,$<) - -#html/split/hgbook.html: $(sources) examples bookhtml.cfg -html/split/hgbook.html: $(sources) bookhtml.cfg - $(call htlatex,$@,$<,2) +.validated-00book.xml: $(xml-src-files) examples/.run + $(xmllint) --path '$(dtd-dir):$(xml-path)' $(xmllint-opts) $< + touch $@ # Produce 90dpi PNGs for the web. -%.png: %.svg - inkscape -D -e $@ $< - -%.svg: %.dot - dot -Tsvg -o $@ $< - -# Produce eps & pdf for the pdf +$(obj-web-read)/figs/%.png: $(obj-web-read)/figs/%.svg fixsvg + mkdir -p $(dir $@) + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg -%.pdf: %.eps - epstopdf $< - -%.eps: %.svg - inkscape -E $@ $< +$(obj-web-read)/figs/%.png: figs/%.svg fixsvg + mkdir -p $(dir $@) + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg -%.eps: %.dot - dot -Tps -o $@ $< +$(obj-web-read)/figs/%.gif: figs/%.gif + cp $< $@ -%.eps: %.png - convert $< ps:$@ +$(obj-web-read)/figs/%.png: figs/%.png + cp $< $@ + +$(obj-web-read)/figs/%.svg: figs/%.dot + mkdir -p $(dir $@) + dot -Tsvg -o $@ $< examples: $(example-prereqs) examples/.run -examples/.run: $(example-sources:%=examples/%.run) - touch examples/.run +examples/.run: $(example-sources) + cd examples && ./run-example -a 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 + -rm -rf dist html $(image-dot:%.dot=%.pdf) $(image-dot:%.dot=%.png) \ + $(image-svg:%.svg=%.png) examples/*.{lxo,run} examples/.run -install: pdf split $(dist-sources) +install: html $(dist-sources) rm -rf dist mkdir -p dist - cp pdf/hgbook.pdf dist - cp html/split/*.{css,html,png} dist + cp html/*.{css,html,png} dist cp $(dist-sources) dist +rsync: install + rsync -avz --delete dist sp.red-bean.com:public_html/hgbook + +vpath %.css ../web +vpath %.html.in ../web +vpath %.js ../web/javascript + +$(obj-websup)/%.css: %.css + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-websup)/%.jpg: %.jpg + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-websup)/%.js: %.js + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-web)/%: ../web/% + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-web)/%.html: %.html.in + @mkdir -p $(dir $@) + python ../web/texpand.py $< $@ + +%.gz: % + gzip -9 -c $< > $@ diff -r 5981a0f7540a -r 019040fbf5f5 en/appA-cmdref.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/appA-cmdref.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,224 @@ + + + + +Command reference + +\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 . + +By default, this command does not print diffs for files that Mercurial +considers to contain binary data. To control this behavior, see the + and options. + + +Options +x +\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, hg diff will refuse to print +diffs for files that it detects as binary. Specifying +forces hg 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 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 + option has no effect unless you change the value of +the showfunc config item, as in the following example. + + + +\optref{diff}{r}{rev} + + +Specify one or more revisions to compare. The hg diff command +accepts up to two options to specify the revisions to +compare. + + + +Display the differences between the parent revision of the + working directory and the working directory. + + +Display the differences between the specified changeset and the + working directory. + + +Display the differences between the two specified changesets. + + + +You can specify two revisions using either two +options or revision range notation. For example, the two revision +specifications below are equivalent. + +hg diff -r 10 -r 20 +hg diff -r10:20 + +When you provide two revisions, Mercurial treats the order of those +revisions as significant. Thus, hg 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 +hg 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. + + +The string unknown. This version of Mercurial was + not built in a Mercurial repository, and cannot determine its own + version. + + +A short numeric string, such as 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.) + + +A hexadecimal string, such as 875489e31abe. This + is a build of the given revision of Mercurial. + + +A hexadecimal string followed by a date, such as + 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. + + + + + +Tips and tricks + + +Why do the results of <command role="hg-cmd">hg diff</command> and <command role="hg-cmd">hg status</command> differ? + +When you run the hg 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 hg diff command, you may notice that it +prints diffs for only a subset of the files that hg status +listed. There are two possible reasons for this. + + +The first is that hg status prints some kinds of modifications +that hg diff doesn't normally display. The hg 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 option to hg diff, it will +display git-compatible diffs that can display this +extra information. + + +The second possible reason that hg diff might be printing diffs +for a subset of the files displayed by hg status is that if you +invoke it without any arguments, hg diff prints diffs against the +first parent of the working directory. If you have run hg merge +to merge two changesets, but you haven't yet committed the results of +the merge, your working directory has two parents (use hg parents +to see them). While hg status prints modifications relative to +both parents after an uncommitted merge, hg 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 + option. There is no way to print diffs relative to +both parents. + + + + +Generating safe binary diffs + +If you use the 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 hg import command or the system's patch +command. + + +If you want to generate a diff of a binary file that is safe to use as +input for hg import, use the hg diff{--git} option when you +generate the patch. The system patch command cannot handle +binary patches at all. + + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/appB-mq-ref.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/appB-mq-ref.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,570 @@ + + + + + Mercurial Queues reference + + + MQ command reference + + For an overview of the commands provided by MQ, use the + command hg help mq. + + + <command role="hg-ext-mq">qapplied</command>&emdash;print + applied patches + + The qapplied command + prints the current stack of applied patches. Patches are + printed in oldest-to-newest order, so the last patch in the + list is the top patch. + + + + <command role="hg-ext-mq">qcommit</command>&emdash;commit + changes in the queue repository + + The qcommit command + commits any outstanding changes in the .hg/patches + repository. This command only works if the .hg/patches + directory is a repository, i.e. you created the directory + using hg qinit or + ran hg init in the directory + after running qinit. + + This command is shorthand for hg + commit --cwd .hg/patches. + + + <command + role="hg-ext-mq">qdelete</command>&emdash;delete a patch + from the <filename role="special">series</filename> + file + + The qdelete command + removes the entry for a patch from the series file in the .hg/patches + directory. It does not pop the patch if the patch is already + applied. By default, it does not delete the patch file; use + the option + to do that. + + Options: + + : Delete the + patch file. + + + + + <command role="hg-ext-mq">qdiff</command>&emdash;print a + diff of the topmost applied patch + + The qdiff command + prints a diff of the topmost applied patch. It is equivalent + to hg diff -r-2:-1. + + + + <command role="hg-ext-mq">qfold</command>&emdash;merge + (<quote>fold</quote>) several patches into one + + The qfold command + merges multiple patches into the topmost applied patch, so + that the topmost applied patch makes the union of all of the + changes in the patches in question. + + The patches to fold must not be applied; qfold will exit with an error if + any is. The order in which patches are folded is significant; + hg qfold a b means + apply the current topmost patch, followed by + a, followed by + b. + + The comments from the folded patches are appended to the + comments of the destination patch, with each block of comments + separated by three asterisk + (*) characters. Use the + option to + edit the commit message for the combined patch/changeset after + the folding has completed. + + Options: + + : Edit the + commit message and patch description for the newly folded + patch. + + : Use the + contents of the given file as the new commit message and + patch description for the folded patch. + + : Use the + given text as the new commit message and patch description + for the folded patch. + + + + + <command + role="hg-ext-mq">qheader</command>&emdash;display the + header/description of a patch + + The qheader command + prints the header, or description, of a patch. By default, it + prints the header of the topmost applied patch. Given an + argument, it prints the header of the named patch. + + + + <command role="hg-ext-mq">qimport</command>&emdash;import + a third-party patch into the queue + + The qimport command + adds an entry for an external patch to the series file, and copies the patch + into the .hg/patches directory. It adds + the entry immediately after the topmost applied patch, but + does not push the patch. + + If the .hg/patches directory is a + repository, qimport + automatically does an hg add + of the imported patch. + + + + <command role="hg-ext-mq">qinit</command>&emdash;prepare + a repository to work with MQ + + The qinit command + prepares a repository to work with MQ. It creates a directory + called .hg/patches. + + Options: + + : Create + .hg/patches as a repository + in its own right. Also creates a .hgignore file that will + ignore the status + file. + + + When the .hg/patches directory is a + repository, the qimport + and qnew commands + automatically hg add new + patches. + + + + <command role="hg-ext-mq">qnew</command>&emdash;create a + new patch + + The qnew command + creates a new patch. It takes one mandatory argument, the + name to use for the patch file. The newly created patch is + created empty by default. It is added to the series file after the current + topmost applied patch, and is immediately pushed on top of + that patch. + + If qnew finds modified + files in the working directory, it will refuse to create a new + patch unless the option is used + (see below). This behavior allows you to qrefresh your topmost applied + patch before you apply a new patch on top of it. + + Options: + + : Create a new + patch if the contents of the working directory are + modified. Any outstanding modifications are added to the + newly created patch, so after this command completes, the + working directory will no longer be modified. + + : Use the given + text as the commit message. This text will be stored at + the beginning of the patch file, before the patch + data. + + + + + <command role="hg-ext-mq">qnext</command>&emdash;print + the name of the next patch + + The qnext command + prints the name name of the next patch in the series file after the topmost + applied patch. This patch will become the topmost applied + patch if you run qpush. + + + + <command role="hg-ext-mq">qpop</command>&emdash;pop + patches off the stack + + The qpop command + removes applied patches from the top of the stack of applied + patches. By default, it removes only one patch. + + This command removes the changesets that represent the + popped patches from the repository, and updates the working + directory to undo the effects of the patches. + + This command takes an optional argument, which it uses as + the name or index of the patch to pop to. If given a name, it + will pop patches until the named patch is the topmost applied + patch. If given a number, qpop treats the number as an + index into the entries in the series file, counting from zero + (empty lines and lines containing only comments do not count). + It pops patches until the patch identified by the given index + is the topmost applied patch. + + The qpop command does + not read or write patches or the series file. It is thus safe to + qpop a patch that you have + removed from the series + file, or a patch that you have renamed or deleted entirely. + In the latter two cases, use the name of the patch as it was + when you applied it. + + By default, the qpop + command will not pop any patches if the working directory has + been modified. You can override this behavior using the + option, + which reverts all modifications in the working + directory. + + Options: + + : Pop all + applied patches. This returns the repository to its state + before you applied any patches. + + : Forcibly + revert any modifications to the working directory when + popping. + + : Pop a patch + from the named queue. + + + The qpop command + removes one line from the end of the status file for each patch that it + pops. + + + + <command role="hg-ext-mq">qprev</command>&emdash;print + the name of the previous patch + + The qprev command + prints the name of the patch in the series file that comes before the + topmost applied patch. This will become the topmost applied + patch if you run qpop. + + + + <command role="hg-ext-mq">qpush</command>&emdash;push + patches onto the stack + + The qpush command adds + patches onto the applied stack. By default, it adds only one + patch. + + This command creates a new changeset to represent each + applied patch, and updates the working directory to apply the + effects of the patches. + + The default data used when creating a changeset are as + follows: + + The commit date and time zone are the current + date and time zone. Because these data are used to + compute the identity of a changeset, this means that if + you qpop a patch and + qpush it again, the + changeset that you push will have a different identity + than the changeset you popped. + + The author is the same as the default used by + the hg commit + command. + + The commit message is any text from the patch + file that comes before the first diff header. If there is + no such text, a default commit message is used that + identifies the name of the patch. + + If a patch contains a Mercurial patch header (XXX add + link), the information in the patch header overrides these + defaults. + + Options: + + : Push all + unapplied patches from the series file until there are + none left to push. + + : Add the name + of the patch to the end of the commit message. + + : If a patch + fails to apply cleanly, use the entry for the patch in + another saved queue to compute the parameters for a + three-way merge, and perform a three-way merge using the + normal Mercurial merge machinery. Use the resolution of + the merge as the new patch content. + + : Use the + named queue if merging while pushing. + + + The qpush command + reads, but does not modify, the series file. It appends one line + to the hg status file for + each patch that it pushes. + + + + <command + role="hg-ext-mq">qrefresh</command>&emdash;update the + topmost applied patch + + The qrefresh command + updates the topmost applied patch. It modifies the patch, + removes the old changeset that represented the patch, and + creates a new changeset to represent the modified + patch. + + The qrefresh command + looks for the following modifications: + + Changes to the commit message, i.e. the text + before the first diff header in the patch file, are + reflected in the new changeset that represents the + patch. + + Modifications to tracked files in the working + directory are added to the patch. + + Changes to the files tracked using hg add, hg copy, hg remove, or hg rename. Added files and copy + and rename destinations are added to the patch, while + removed files and rename sources are removed. + + + Even if qrefresh + detects no changes, it still recreates the changeset that + represents the patch. This causes the identity of the + changeset to differ from the previous changeset that + identified the patch. + + Options: + + : Modify + the commit and patch description, using the preferred text + editor. + + : Modify + the commit message and patch description, using the given + text. + + : Modify + the commit message and patch description, using text from + the given file. + + + + + <command role="hg-ext-mq">qrename</command>&emdash;rename + a patch + + The qrename command + renames a patch, and changes the entry for the patch in the + series file. + + With a single argument, qrename renames the topmost + applied patch. With two arguments, it renames its first + argument to its second. + + + + <command + role="hg-ext-mq">qrestore</command>&emdash;restore saved + queue state + + XXX No idea what this does. + + + + <command role="hg-ext-mq">qsave</command>&emdash;save + current queue state + + XXX Likewise. + + + + <command role="hg-ext-mq">qseries</command>&emdash;print + the entire patch series + + The qseries command + prints the entire patch series from the series file. It prints only patch + names, not empty lines or comments. It prints in order from + first to be applied to last. + + + + <command role="hg-ext-mq">qtop</command>&emdash;print the + name of the current patch + + The qtop prints the + name of the topmost currently applied patch. + + + + <command + role="hg-ext-mq">qunapplied</command>&emdash;print patches + not yet applied + + The qunapplied command + prints the names of patches from the series file that are not yet + applied. It prints them in order from the next patch that + will be pushed to the last. + + + + <command role="hg-cmd">hg strip</command>&emdash;remove a + revision and descendants + + The hg strip command + removes a revision, and all of its descendants, from the + repository. It undoes the effects of the removed revisions + from the repository, and updates the working directory to the + first parent of the removed revision. + + The hg strip command + saves a backup of the removed changesets in a bundle, so that + they can be reapplied if removed in error. + + Options: + + : Save + unrelated changesets that are intermixed with the stripped + changesets in the backup bundle. + + : If a + branch has multiple heads, remove all heads. XXX This + should be renamed, and use -f to strip + revs when there are pending changes. + + : Do + not save a backup bundle. + + + + + + MQ file reference + + + The <filename role="special">series</filename> + file + + The series file + contains a list of the names of all patches that MQ can apply. + It is represented as a list of names, with one name saved per + line. Leading and trailing white space in each line are + ignored. + + Lines may contain comments. A comment begins with the + # character, and extends to + the end of the line. Empty lines, and lines that contain only + comments, are ignored. + + You will often need to edit the series file by hand, hence the + support for comments and empty lines noted above. For + example, you can comment out a patch temporarily, and qpush will skip over that patch + when applying patches. You can also change the order in which + patches are applied by reordering their entries in the + series file. + + Placing the series + file under revision control is also supported; it is a good + idea to place all of the patches that it refers to under + revision control, as well. If you create a patch directory + using the + option to qinit, this will + be done for you automatically. + + + + The <filename role="special">status</filename> + file + + The status file + contains the names and changeset hashes of all patches that MQ + currently has applied. Unlike the series file, this file is not + intended for editing. You should not place this file under + revision control, or modify it in any way. It is used by MQ + strictly for internal book-keeping. + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/appC-srcinstall.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/appC-srcinstall.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,66 @@ + + + + + Installing Mercurial from source + + + On a Unix-like system + + If you are using a Unix-like system that has a sufficiently + recent version of Python (2.3 or newer) available, it is easy to + install Mercurial from source. + + Download a recent source tarball from http://www.selenic.com/mercurial/download. + + Unpack the tarball: + gzip -dc mercurial-MYVERSION.tar.gz | tar xf - + + Go into the source directory and run the + installer script. This will build Mercurial and install it + in your home directory. + cd mercurial-MYVERSION +python setup.py install --force --home=$HOME + + + Once the install finishes, Mercurial will be in the + bin subdirectory of your home directory. + Don't forget to make sure that this directory is present in your + shell's search path. + + You will probably need to set the PYTHONPATH + environment variable so that the Mercurial executable can find + the rest of the Mercurial packages. For example, on my laptop, + I have set it to /home/bos/lib/python. The + exact path that you will need to use depends on how Python was + built for your system, but should be easy to figure out. If + you're uncertain, look through the output of the installer + script above, and see where the contents of the + mercurial directory were installed to. + + + + On Windows + + Building and installing Mercurial on Windows requires a + variety of tools, a fair amount of technical knowledge, and + considerable patience. I very much do not + recommend this route if you are a casual + user. Unless you intend to hack on Mercurial, I + strongly suggest that you use a binary package instead. + + If you are intent on building Mercurial from source on + Windows, follow the hard way directions on the + Mercurial wiki at http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall, + and expect the process to involve a lot of fiddly work. + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/appD-license.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/appD-license.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,179 @@ + + + + + Open Publication License + + Version 1.0, 8 June 1999 + + + Requirements on both unmodified and modified + versions + + The Open Publication works may be reproduced and distributed + in whole or in part, in any medium physical or electronic, + provided that the terms of this license are adhered to, and that + this license or an incorporation of it by reference (with any + options elected by the author(s) and/or publisher) is displayed + in the reproduction. + + Proper form for an incorporation by reference is as + follows: + +
+ Copyright (c) year by + author's name or designee. This material + may be distributed only subject to the terms and conditions + set forth in the Open Publication License, + vx.y or later (the latest version is + presently available at http://www.opencontent.org/openpub/). +
+ + The reference must be immediately followed with any options + elected by the author(s) and/or publisher of the document (see + ). + + Commercial redistribution of Open Publication-licensed + material is permitted. + + Any publication in standard (paper) book form shall require + the citation of the original publisher and author. The publisher + and author's names shall appear on all outer surfaces of the + book. On all outer surfaces of the book the original publisher's + name shall be as large as the title of the work and cited as + possessive with respect to the title. + +
+ + Copyright + + The copyright to each Open Publication is owned by its + author(s) or designee. + + + + Scope of license + + The following license terms apply to all Open Publication + works, unless otherwise explicitly stated in the + document. + + Mere aggregation of Open Publication works or a portion of + an Open Publication work with other works or programs on the + same media shall not cause this license to apply to those other + works. The aggregate work shall contain a notice specifying the + inclusion of the Open Publication material and appropriate + copyright notice. + + Severability. If any part + of this license is found to be unenforceable in any + jurisdiction, the remaining portions of the license remain in + force. + + No warranty. Open + Publication works are licensed and provided as is + without warranty of any kind, express or implied, including, but + not limited to, the implied warranties of merchantability and + fitness for a particular purpose or a warranty of + non-infringement. + + + + Requirements on modified works + + All modified versions of documents covered by this license, + including translations, anthologies, compilations and partial + documents, must meet the following requirements: + + + The modified version must be labeled as + such. + + The person making the modifications must be + identified and the modifications dated. + + Acknowledgement of the original author and + publisher if applicable must be retained according to normal + academic citation practices. + + The location of the original unmodified document + must be identified. + + The original author's (or authors') name(s) may + not be used to assert or imply endorsement of the resulting + document without the original author's (or authors') + permission. + + + + + Good-practice recommendations + + In addition to the requirements of this license, it is + requested from and strongly recommended of redistributors + that: + + + If you are distributing Open Publication works + on hardcopy or CD-ROM, you provide email notification to the + authors of your intent to redistribute at least thirty days + before your manuscript or media freeze, to give the authors + time to provide updated documents. This notification should + describe modifications, if any, made to the document. + + All substantive modifications (including + deletions) be either clearly marked up in the document or + else described in an attachment to the document. + + Finally, while it is not mandatory under this + license, it is considered good form to offer a free copy of + any hardcopy and CD-ROM expression of an Open + Publication-licensed work to its author(s). + + + + + License options + + The author(s) and/or publisher of an Open + Publication-licensed document may elect certain options by + appending language to the reference to or copy of the license. + These options are considered part of the license instance and + must be included with the license (or its incorporation by + reference) in derived works. + + + To prohibit distribution of substantively + modified versions without the explicit permission of the + author(s). Substantive modification is + defined as a change to the semantic content of the document, + and excludes mere changes in format or typographical + corrections. + + To accomplish this, add the phrase + Distribution of substantively modified versions of + this document is prohibited without the explicit + permission of the copyright holder. to the license + reference or copy. + + To prohibit any publication of this work or + derivative works in whole or in part in standard (paper) + book form for commercial purposes is prohibited unless prior + permission is obtained from the copyright holder. + + To accomplish this, add the phrase + Distribution of the work or derivative of the work in + any standard (paper) book form is prohibited unless prior + permission is obtained from the copyright holder. + to the license reference or copy. + + + +
+ + diff -r 5981a0f7540a -r 019040fbf5f5 en/autoid.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/autoid.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Add unique ID attributes to para tags. This script should only be +# run by one person, since otherwise it introduces the possibility of +# chaotic conflicts among tags. + +import glob, os, re, sys + +tagged = re.compile(']* id="x_([0-9a-f]+)"[^>]*>', re.M) +untagged = re.compile('') + +names = glob.glob('ch*.xml') + glob.glob('app*.xml') + +# First pass: find the highest-numbered paragraph ID. + +biggest_id = 0 +seen = set() +errs = 0 + +for name in names: + for m in tagged.finditer(open(name).read()): + i = int(m.group(1),16) + if i in seen: + print >> sys.stderr, '%s: duplication of ID %s' % (name, i) + errs += 1 + seen.add(i) + if i > biggest_id: + biggest_id = i + +def retag(s): + global biggest_id + biggest_id += 1 + return '' % biggest_id + +# Second pass: add IDs to paragraphs that currently lack them. + +for name in names: + f = open(name).read() + f1 = untagged.sub(retag, f) + if f1 != f: + tmpname = name + '.tmp' + fp = open(tmpname, 'w') + fp.write(f1) + fp.close() + os.rename(tmpname, name) + +sys.exit(errs) diff -r 5981a0f7540a -r 019040fbf5f5 en/book-shortcuts.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/book-shortcuts.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,3 @@ + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/bookhtml.cfg --- a/en/bookhtml.cfg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -% -*- latex -*- - -\Preamble{xhtml} - -% Tex4ht's default definition of lists is complete crap. -% Unfortunately, it can't distinguish between "ul" and "dl" lists. - -\ConfigureList{itemize}% - {\EndP\HCode{
    }\let\endItem=\empty} - {\ifvmode \IgnorePar\fi - \EndP\HCode{
}\ShowPar} - {\endItem \def\endItem{\EndP\Tg}\HCode{
  • }} - {\HCode{}} -\def\textbullet{} - -\begin{document} - -\EndPreamble diff -r 5981a0f7540a -r 019040fbf5f5 en/branch.tex --- a/en/branch.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,396 +0,0 @@ -\chapter{Managing releases and branchy development} -\label{chap:branch} - -Mercurial provides several mechanisms for you to manage a project that -is making progress on multiple fronts at once. To understand these -mechanisms, let's first take a brief look at a fairly normal software -project structure. - -Many software projects issue periodic ``major'' releases that contain -substantial new features. In parallel, they may issue ``minor'' -releases. These are usually identical to the major releases off which -they're based, but with a few bugs fixed. - -In this chapter, we'll start by talking about how to keep records of -project milestones such as releases. We'll then continue on to talk -about the flow of work between different phases of a project, and how -Mercurial can help you to isolate and manage this work. - -\section{Giving a persistent name to a revision} - -Once you decide that you'd like to call a particular revision a -``release'', it's a good idea to record the identity of that revision. -This will let you reproduce that release at a later date, for whatever -purpose you might need at the time (reproducing a bug, porting to a -new platform, etc). -\interaction{tag.init} - -Mercurial lets you give a permanent name to any revision using the -\hgcmd{tag} command. Not surprisingly, these names are called -``tags''. -\interaction{tag.tag} - -A tag is nothing more than a ``symbolic name'' for a revision. Tags -exist purely for your convenience, so that you have a handy permanent -way to refer to a revision; Mercurial doesn't interpret the tag names -you use in any way. Neither does Mercurial place any restrictions on -the name of a tag, beyond a few that are necessary to ensure that a -tag can be parsed unambiguously. A tag name cannot contain any of the -following characters: -\begin{itemize} -\item Colon (ASCII 58, ``\texttt{:}'') -\item Carriage return (ASCII 13, ``\Verb+\r+'') -\item Newline (ASCII 10, ``\Verb+\n+'') -\end{itemize} - -You can use the \hgcmd{tags} command to display the tags present in -your repository. In the output, each tagged revision is identified -first by its name, then by revision number, and finally by the unique -hash of the revision. -\interaction{tag.tags} -Notice that \texttt{tip} is listed in the output of \hgcmd{tags}. The -\texttt{tip} tag is a special ``floating'' tag, which always -identifies the newest revision in the repository. - -In the output of the \hgcmd{tags} command, tags are listed in reverse -order, by revision number. This usually means that recent tags are -listed before older tags. It also means that \texttt{tip} is always -going to be the first tag listed in the output of \hgcmd{tags}. - -When you run \hgcmd{log}, if it displays a revision that has tags -associated with it, it will print those tags. -\interaction{tag.log} - -Any time you need to provide a revision~ID to a Mercurial command, the -command will accept a tag name in its place. Internally, Mercurial -will translate your tag name into the corresponding revision~ID, then -use that. -\interaction{tag.log.v1.0} - -There's no limit on the number of tags you can have in a repository, -or on the number of tags that a single revision can have. As a -practical matter, it's not a great idea to have ``too many'' (a number -which will vary from project to project), simply because tags are -supposed to help you to find revisions. If you have lots of tags, the -ease of using them to identify revisions diminishes rapidly. - -For example, if your project has milestones as frequent as every few -days, it's perfectly reasonable to tag each one of those. But if you -have a continuous build system that makes sure every revision can be -built cleanly, you'd be introducing a lot of noise if you were to tag -every clean build. Instead, you could tag failed builds (on the -assumption that they're rare!), or simply not use tags to track -buildability. - -If you want to remove a tag that you no longer want, use -\hgcmdargs{tag}{--remove}. -\interaction{tag.remove} -You can also modify a tag at any time, so that it identifies a -different revision, by simply issuing a new \hgcmd{tag} command. -You'll have to use the \hgopt{tag}{-f} option to tell Mercurial that -you \emph{really} want to update the tag. -\interaction{tag.replace} -There will still be a permanent record of the previous identity of the -tag, but Mercurial will no longer use it. There's thus no penalty to -tagging the wrong revision; all you have to do is turn around and tag -the correct revision once you discover your error. - -Mercurial stores tags in a normal revision-controlled file in your -repository. If you've created any tags, you'll find them in a file -named \sfilename{.hgtags}. When you run the \hgcmd{tag} command, -Mercurial modifies this file, then automatically commits the change to -it. This means that every time you run \hgcmd{tag}, you'll see a -corresponding changeset in the output of \hgcmd{log}. -\interaction{tag.tip} - -\subsection{Handling tag conflicts during a merge} - -You won't often need to care about the \sfilename{.hgtags} file, but -it sometimes makes its presence known during a merge. The format of -the file is simple: it consists of a series of lines. Each line -starts with a changeset hash, followed by a space, followed by the -name of a tag. - -If you're resolving a conflict in the \sfilename{.hgtags} file during -a merge, there's one twist to modifying the \sfilename{.hgtags} file: -when Mercurial is parsing the tags in a repository, it \emph{never} -reads the working copy of the \sfilename{.hgtags} file. Instead, it -reads the \emph{most recently committed} revision of the file. - -An unfortunate consequence of this design is that you can't actually -verify that your merged \sfilename{.hgtags} file is correct until -\emph{after} you've committed a change. So if you find yourself -resolving a conflict on \sfilename{.hgtags} during a merge, be sure to -run \hgcmd{tags} after you commit. If it finds an error in the -\sfilename{.hgtags} file, it will report the location of the error, -which you can then fix and commit. You should then run \hgcmd{tags} -again, just to be sure that your fix is correct. - -\subsection{Tags and cloning} - -You may have noticed that the \hgcmd{clone} command has a -\hgopt{clone}{-r} option that lets you clone an exact copy of the -repository as of a particular changeset. The new clone will not -contain any project history that comes after the revision you -specified. This has an interaction with tags that can surprise the -unwary. - -Recall that a tag is stored as a revision to the \sfilename{.hgtags} -file, so that when you create a tag, the changeset in which it's -recorded necessarily refers to an older changeset. When you run -\hgcmdargs{clone}{-r foo} to clone a repository as of tag -\texttt{foo}, the new clone \emph{will not contain the history that - created the tag} that you used to clone the repository. The result -is that you'll get exactly the right subset of the project's history -in the new repository, but \emph{not} the tag you might have expected. - -\subsection{When permanent tags are too much} - -Since Mercurial's tags are revision controlled and carried around with -a project's history, everyone you work with will see the tags you -create. But giving names to revisions has uses beyond simply noting -that revision \texttt{4237e45506ee} is really \texttt{v2.0.2}. If -you're trying to track down a subtle bug, you might want a tag to -remind you of something like ``Anne saw the symptoms with this -revision''. - -For cases like this, what you might want to use are \emph{local} tags. -You can create a local tag with the \hgopt{tag}{-l} option to the -\hgcmd{tag} command. This will store the tag in a file called -\sfilename{.hg/localtags}. Unlike \sfilename{.hgtags}, -\sfilename{.hg/localtags} is not revision controlled. Any tags you -create using \hgopt{tag}{-l} remain strictly local to the repository -you're currently working in. - -\section{The flow of changes---big picture vs. little} - -To return to the outline I sketched at the beginning of a chapter, -let's think about a project that has multiple concurrent pieces of -work under development at once. - -There might be a push for a new ``main'' release; a new minor bugfix -release to the last main release; and an unexpected ``hot fix'' to an -old release that is now in maintenance mode. - -The usual way people refer to these different concurrent directions of -development is as ``branches''. However, we've already seen numerous -times that Mercurial treats \emph{all of history} as a series of -branches and merges. Really, what we have here is two ideas that are -peripherally related, but which happen to share a name. -\begin{itemize} -\item ``Big picture'' branches represent the sweep of a project's - evolution; people give them names, and talk about them in - conversation. -\item ``Little picture'' branches are artefacts of the day-to-day - activity of developing and merging changes. They expose the - narrative of how the code was developed. -\end{itemize} - -\section{Managing big-picture branches in repositories} - -The easiest way to isolate a ``big picture'' branch in Mercurial is in -a dedicated repository. If you have an existing shared -repository---let's call it \texttt{myproject}---that reaches a ``1.0'' -milestone, you can start to prepare for future maintenance releases on -top of version~1.0 by tagging the revision from which you prepared -the~1.0 release. -\interaction{branch-repo.tag} -You can then clone a new shared \texttt{myproject-1.0.1} repository as -of that tag. -\interaction{branch-repo.clone} - -Afterwards, if someone needs to work on a bug fix that ought to go -into an upcoming~1.0.1 minor release, they clone the -\texttt{myproject-1.0.1} repository, make their changes, and push them -back. -\interaction{branch-repo.bugfix} -Meanwhile, development for the next major release can continue, -isolated and unabated, in the \texttt{myproject} repository. -\interaction{branch-repo.new} - -\section{Don't repeat yourself: merging across branches} - -In many cases, if you have a bug to fix on a maintenance branch, the -chances are good that the bug exists on your project's main branch -(and possibly other maintenance branches, too). It's a rare developer -who wants to fix the same bug multiple times, so let's look at a few -ways that Mercurial can help you to manage these bugfixes without -duplicating your work. - -In the simplest instance, all you need to do is pull changes from your -maintenance branch into your local clone of the target branch. -\interaction{branch-repo.pull} -You'll then need to merge the heads of the two branches, and push back -to the main branch. -\interaction{branch-repo.merge} - -\section{Naming branches within one repository} - -In most instances, isolating branches in repositories is the right -approach. Its simplicity makes it easy to understand; and so it's -hard to make mistakes. There's a one-to-one relationship between -branches you're working in and directories on your system. This lets -you use normal (non-Mercurial-aware) tools to work on files within a -branch/repository. - -If you're more in the ``power user'' category (\emph{and} your -collaborators are too), there is an alternative way of handling -branches that you can consider. I've already mentioned the -human-level distinction between ``small picture'' and ``big picture'' -branches. While Mercurial works with multiple ``small picture'' -branches in a repository all the time (for example after you pull -changes in, but before you merge them), it can \emph{also} work with -multiple ``big picture'' branches. - -The key to working this way is that Mercurial lets you assign a -persistent \emph{name} to a branch. There always exists a branch -named \texttt{default}. Even before you start naming branches -yourself, you can find traces of the \texttt{default} branch if you -look for them. - -As an example, when you run the \hgcmd{commit} command, and it pops up -your editor so that you can enter a commit message, look for a line -that contains the text ``\texttt{HG: branch default}'' at the bottom. -This is telling you that your commit will occur on the branch named -\texttt{default}. - -To start working with named branches, use the \hgcmd{branches} -command. This command lists the named branches already present in -your repository, telling you which changeset is the tip of each. -\interaction{branch-named.branches} -Since you haven't created any named branches yet, the only one that -exists is \texttt{default}. - -To find out what the ``current'' branch is, run the \hgcmd{branch} -command, giving it no arguments. This tells you what branch the -parent of the current changeset is on. -\interaction{branch-named.branch} - -To create a new branch, run the \hgcmd{branch} command again. This -time, give it one argument: the name of the branch you want to create. -\interaction{branch-named.create} - -After you've created a branch, you might wonder what effect the -\hgcmd{branch} command has had. What do the \hgcmd{status} and -\hgcmd{tip} commands report? -\interaction{branch-named.status} -Nothing has changed in the working directory, and there's been no new -history created. As this suggests, running the \hgcmd{branch} command -has no permanent effect; it only tells Mercurial what branch name to -use the \emph{next} time you commit a changeset. - -When you commit a change, Mercurial records the name of the branch on -which you committed. Once you've switched from the \texttt{default} -branch to another and committed, you'll see the name of the new branch -show up in the output of \hgcmd{log}, \hgcmd{tip}, and other commands -that display the same kind of output. -\interaction{branch-named.commit} -The \hgcmd{log}-like commands will print the branch name of every -changeset that's not on the \texttt{default} branch. As a result, if -you never use named branches, you'll never see this information. - -Once you've named a branch and committed a change with that name, -every subsequent commit that descends from that change will inherit -the same branch name. You can change the name of a branch at any -time, using the \hgcmd{branch} command. -\interaction{branch-named.rebranch} -In practice, this is something you won't do very often, as branch -names tend to have fairly long lifetimes. (This isn't a rule, just an -observation.) - -\section{Dealing with multiple named branches in a repository} - -If you have more than one named branch in a repository, Mercurial will -remember the branch that your working directory on when you start a -command like \hgcmd{update} or \hgcmdargs{pull}{-u}. It will update -the working directory to the tip of this branch, no matter what the -``repo-wide'' tip is. To update to a revision that's on a different -named branch, you may need to use the \hgopt{update}{-C} option to -\hgcmd{update}. - -This behaviour is a little subtle, so let's see it in action. First, -let's remind ourselves what branch we're currently on, and what -branches are in our repository. -\interaction{branch-named.parents} -We're on the \texttt{bar} branch, but there also exists an older -\hgcmd{foo} branch. - -We can \hgcmd{update} back and forth between the tips of the -\texttt{foo} and \texttt{bar} branches without needing to use the -\hgopt{update}{-C} option, because this only involves going backwards -and forwards linearly through our change history. -\interaction{branch-named.update-switchy} - -If we go back to the \texttt{foo} branch and then run \hgcmd{update}, -it will keep us on \texttt{foo}, not move us to the tip of -\texttt{bar}. -\interaction{branch-named.update-nothing} - -Committing a new change on the \texttt{foo} branch introduces a new -head. -\interaction{branch-named.foo-commit} -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 5981a0f7540a -r 019040fbf5f5 en/ch00-preface.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch00-preface.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,757 @@ + + + + + Preface + + + Why revision control? Why Mercurial? + + 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. + + The arrival of distributed revision control is relatively + recent, and so far this new field has grown due to people's + willingness to explore 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 buckle. + + + 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. + + + 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 who made it; + why they made it; + when they made it; and + what the change + was. + 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. + 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 really good revision control + tool will even help you to efficiently figure out exactly + when a problem was introduced (see for details). + It will help you to work simultaneously on, + and manage the drift between, multiple versions of your + project. + + + Most of these reasons are equally + valid&emdash;at least in theory&emdash;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 + benefits compare to its + 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 + 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 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 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. + + + + + The many names of revision control + + Revision control is a diverse field, so much so that it is + referred to by many names and acronyms. Here are a few of the + more common variations you'll encounter: + + Revision control (RCS) + Software configuration management (SCM), or + configuration management + Source code management + Source code control, or source + control + Version control + (VCS) + 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. + + + + + + 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 am writing under an + open license in the hope that you, my readers, will contribute + feedback and perhaps content of your own. + + + + About the examples in this book + + This book takes an unusual approach to code samples. Every + example is live&emdash;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 exactly the + behavior 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 bisect example in , 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 + do be confident that the behavior you're + seeing is consistent and reproducible. + + + + + Trends in the field + + 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. + + + + 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. + + + 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. + + + 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 + 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 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 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 purely a social issue. If + anything, distributed tools lower the + likelihood of a fork: + + They eliminate the social distinction that + centralised tools impose: that between insiders (people + with commit access) and outsiders (people + without). + 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. + + 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 publicly, 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. + + + + + 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&emdash;if you have + one at all&emdash;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. + + + + + Why choose Mercurial? + + Mercurial has a unique set of properties that make it a + particularly good choice as a revision control system. + + It is easy to learn and use. + It is lightweight. + It scales excellently. + It is easy to + customise. + + 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. + + + + 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. + + + + 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. + + Prior to version 1.5, Subversion had no useful support for + merges. At the time of writing, its merge tracking capability + is new, and known to be complicated + and buggy. + + 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 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 + (status) and displaying modifications + against the current revision (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. + + + + + 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. + + + + + 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. + + + + + 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. + + + + + 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. + + 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. + + + + + + Switching from another tool to Mercurial + + Mercurial is bundled with an extension named 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 convert are as follows: + + Subversion + CVS + Git + Darcs + + In addition, 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 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. + + + + 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 (Revision 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. + + + + + Colophon&emdash;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 DocBook XML. Illustrations are drawn and rendered with + Inkscape. + + The complete source code for this book is published as a + Mercurial repository, at http://hg.serpentine.com/mercurial/book. + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch01-tour-basic.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch01-tour-basic.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,930 @@ + + + + + A tour of Mercurial: the basics + + + Installing Mercurial on your system + + Prebuilt binary packages of Mercurial are available for + every popular operating system. These make it easy to start + using Mercurial on your computer immediately. + + + Windows + + The best version of Mercurial for Windows is + TortoiseHg, which can be found at http://bitbucket.org/tortoisehg/stable/wiki/Home. + This package has no external dependencies; it just + works. It provides both command line and graphical + user interfaces. + + + + + Mac OS X + + Lee Cantey publishes an installer of Mercurial + for Mac OS X at http://mercurial.berkwood.com. + + + + 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 + mercurial. + + + Ubuntu and Debian: + apt-get install mercurial + Fedora and OpenSUSE: + yum install mercurial + Gentoo: + emerge mercurial + + + + + Solaris + + SunFreeWare, at http://www.sunfreeware.com, + provides prebuilt packages of Mercurial. + + + + + + + Getting started + + To begin, we'll use the hg + 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; + + + 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 hg + 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 hg help . The option is short for + , and tells + Mercurial to print more information than it usually + would. + + + + + Working with a repository + + In Mercurial, everything happens inside a + 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. + + + Making a local copy of a repository + + 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 hg clone, because it + makes an identical copy of an existing repository. + + &interaction.tour.clone; + + One advantage of using hg + clone is that, as we can see above, it lets us clone + repositories over the network. Another is that it remembers + where we cloned from, which we'll find useful soon when we + want to fetch new changes from another repository. + + If our clone succeeded, we should now have a local + directory called 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. As we just mentioned, + a cloned repository remembers the location of the repository + it was cloned from, but Mercurial will 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. + + + + What's in a repository? + + When we take a more detailed look inside a repository, we + can see that it contains a directory named .hg. This is where Mercurial + keeps all of its metadata for the repository. + + &interaction.tour.ls-a; + + The contents of the .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 .hg directory is the + real repository, and all of the files and + directories that coexist with it are said to live in the + working directory. An easy way to + remember the distinction is that the + repository contains the + history of your project, while the + working directory contains a + snapshot of your project at a particular + point in history. + + + + + A tour through history + + One of the first things we might want to do with a new, + unfamiliar repository is understand its history. The hg log command gives us a view of + the history of changes in the repository. + + &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 + changeset, because it can contain a record + of changes to several files. + + The fields in a record of output from hg log are as follows. + + + changeset: This + field has the format of a number, followed by a colon, + followed by a hexadecimal (or hex) + string. These are identifiers for the + changeset. The hex string is a unique identifier: the same + hex string will always refer to the same changeset. The + number is shorter and easier to type than the hex string, + but it isn't unique: the same number in two different clones + of a repository may identify different changesets. Why + provide the number at all, then? For local + convenience. + + 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. + 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.) + summary: The first line of + the text message that the creator of the changeset entered + to describe the changeset. + + Some changesets, such as the first in the list above, + have a tag field. A tag is another way + to identify a changeset, by giving it an easy-to-remember + name. (The tag named tip is special: it + always refers to the newest change in a repository.) + + + + The default output printed by hg log is purely a summary; it is + missing a lot of detail. + + provides + a graphical representation of the history of the 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. + +
    + Graphical history of the <filename + class="directory">hello</filename> repository + + + XXX add text + +
    + + + 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 word you + use to refer to the concept of a changeset, the + identifier that you use to refer to + a specific changeset is of + great importance. Recall that the changeset + field in the output from hg + log identifies a changeset using both a number and + a hexadecimal string. + + The revision number is a handy + notation that is only valid in that + repository. + The hexadecimal string is the + permanent, unchanging identifier that + will always identify that exact changeset in + every copy of the + repository. + + This distinction is important. If you send + someone an email talking about revision 33, + there's a high likelihood that their revision 33 will + 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 + 0,2,1. + + 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. + + + + Viewing specific revisions + + To narrow the output of hg + log down to a single revision, use the (or ) option. You can use + either a revision number or a hexadecimal 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 range + notation; this lets you express the idea I + want all revisions between abc and + def, inclusive. + + &interaction.tour.log.range; + + Mercurial also honours the order in which you specify + revisions, so hg log -r 2:4 + prints 2, 3, and 4. while hg log -r + 4:2 prints 4, 3, and 2. + + + + More detailed information + + While the summary information printed by hg 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 hg log + command's (or ) 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 (or ) option. This displays + the content of a change as a unified diff + (if you've never seen a unified diff before, see for an overview). + + &interaction.tour.log-vp; + + The option is + tremendously useful, so it's well worth remembering. + + +
    + + + 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. + + + + Every option has a long name. For example, as + we've already seen, the hg + log command accepts a option. + + + Most options have short names, too. Instead + of , we can use + . (The reason that + some options don't have short names is that the options in + question are rarely used.) + + + Long options start with two dashes (e.g. + ), while short + options start with one (e.g. ). + + + Option naming and usage is consistent across + commands. For example, every command that lets you specify + a changeset ID or revision number accepts both and arguments. + + + If you are using short options, you can save typing by + running them together. For example, the command hg log -v -p -r 2 can be written + as hg log -vpr2. + + + + 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 + (or ) option, and + less when passed (or + ). + + + Option naming consistency + + Almost always, Mercurial commands use consistent option + names to refer to the same concepts. For instance, if a + command deals with changesets, you'll always identify them + with or . This consistent use of + option names makes it easier to remember what options a + particular command takes. + + + + + 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 hg + 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 + The saving of space arises when source and destination + repositories are on the same filesystem, in which case + Mercurial will use hardlinks to do copy-on-write sharing of + its internal metadata. If that explanation meant nothing to + you, don't worry: everything happens transparently and + automatically, and you don't need to understand it. + . + + &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 my-hello + repository, we have a file hello.c that + contains the classic hello, world program. + + &interaction.tour.cat1; + + Let's edit this file so that it prints a second line of + output. + + &interaction.tour.cat2; + + Mercurial's hg status + command will tell us what Mercurial knows about the files in the + repository. + + &interaction.tour.status; + + The hg status command + prints no output for some files, but a line starting with + M for + hello.c. Unless you tell it to, hg status will not print any output + for files that have not been modified. + + The M indicates that + Mercurial has noticed that we modified + hello.c. We didn't need to + 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 somewhat helpful to know that we've modified + hello.c, but we might prefer to know + exactly what changes we've made to it. To + do this, we use the hg diff + command. + + &interaction.tour.diff; + + + Understanding patches + + Remember to take a look at if you don't know how to read + output above. + + + + Recording changes in a new changeset + + We can modify files, build and test our changes, and use + hg status and hg 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 hg commit command lets + us create a new changeset; we'll usually refer to this as + making a commit or + committing. + + + Setting up a username + + When you try to run hg + 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: + + If you specify a option to the hg commit command on the command + line, followed by a username, this is always given the + highest precedence. + If you have set the HGUSER + environment variable, this is checked + next. + If you create a file in your home + directory called .hgrc, with a username entry, that will be + used next. To see what the contents of this file should + look like, refer to + below. + If you have set the EMAIL + environment variable, this will be used + next. + 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. + + 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 HGUSER environment + variable and the + option to the hg commit + command as ways to 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 .hgrc file; see + below for details. + + Creating a Mercurial configuration file + + To set a user name, use your favorite editor + to create a file called .hgrc in your home directory. + Mercurial will use this file to look up your personalised + configuration settings. The initial contents of your + .hgrc should look like + this. + + Figure out what the appropriate directory is on + Windows. + + # This is a Mercurial configuration file. +[ui] +username = Firstname Lastname <email.address@domain.net> + + The [ui] line begins a + section of the config file, so you can + read the username = ... + line as meaning set the value of the + username item in the + 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 + # to the end of a line as + a comment. + + + + Choosing a user name + + You can use any text you like as the value of + the username config item, since this + information is for reading by other people, but will not be + interpreted by Mercurial. The convention that most + people follow is to use their name and email address, as + in the example above. + + 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. + + + + + 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 commit message. It will be a + record for readers of what we did and why, and it will be + printed by hg log after + we've finished committing. + + &interaction.tour.commit; + + The editor that the hg + commit command drops us into will contain an + empty line or two, followed by a number of lines starting with + HG:. + + +This is where I type my commit comment. + +HG: Enter commit message. Lines beginning with 'HG:' are removed. +HG: -- +HG: user: Bryan O'Sullivan <bos@serpentine.com> +HG: branch 'default' +HG: changed hello.c + + Mercurial ignores the lines that start with + HG:; it uses them only to + tell us which files it's recording changes to. Modifying or + deleting these lines has no effect. + + + Writing a good commit message + + Since hg 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 + doesn't follow this guideline, and + hence has a summary that is not + readable. + + +changeset: 73:584af0e231be +user: Censored Person <censored.person@example.org> +date: Tue Sep 26 21:37:07 2006 -0700 +summary: include buildmeister/commondefs. Add exports. + + 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 + hg log + --patch. + + + 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 hg + commit command without any arguments, it records + all of the changes we've made, as reported by hg status and hg diff. + + + Admiring our new handiwork + + Once we've finished the commit, we can use the + hg tip command to display + the changeset we just created. This command produces output + that is identical to hg + 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. + + By the way, the hg tip + command accepts many of the same options as hg log, so above indicates be + verbose, + specifies print a patch. The use of to print patches is another + example of the consistent naming we mentioned earlier. + + + + + Sharing changes + + We mentioned earlier that repositories in + Mercurial are self-contained. This means that the changeset + we just created exists only in our my-hello repository. Let's + look at a few ways that we can propagate this change into + other repositories. + + + Pulling changes from another repository + To get started, let's clone our original + hello repository, + which does not contain the change we just committed. We'll + call our temporary repository hello-pull. + + &interaction.tour.clone-pull; + + We'll use the hg + pull command to bring changes from my-hello into hello-pull. However, blindly + pulling unknown changes into a repository is a somewhat + scary prospect. Mercurial provides the hg incoming command to tell us + what changes the hg pull + command would pull into the repository, + without actually pulling the changes in. + + &interaction.tour.incoming; + + Suppose you're pulling changes from a repository + on the network somewhere. While you are looking at the hg incoming output, and before you + pull those changes, someone might have committed something in + the remote repository. This means that it's possible to pull + more changes than you saw when using hg incoming. + + Bringing changes into a repository is a simple + matter of running the hg + pull command, and telling it which repository to + pull from. + + &interaction.tour.pull; + + As you can see + from the before-and-after output of hg tip, we have successfully + pulled changes into our repository. There remains one step + before we can see these changes in the working + directory. + + + Updating the working directory + + We have so far glossed over the relationship + between a repository and its working directory. The hg pull command that we ran in + brought changes into the + repository, but if we check, there's no sign of those changes + in the working directory. This is because hg pull does not (by default) touch + the working directory. Instead, we use the hg update command to do this. + + &interaction.tour.update; + + It might seem a bit strange that hg + pull doesn't update the working directory + automatically. There's actually a good reason for this: you + can use hg update to update + the working directory to the state it was in at any + revision in the history of the repository. If + you had the working directory updated to an old revision&emdash;to + hunt down the origin of a bug, say&emdash;and ran a hg 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 option to hg pull. + + If you look back at the output of hg pull in when we ran it without , you can see that it printed + a helpful reminder that we'd have to take an explicit step to + update the working directory: + + + + To find out what revision the working directory is at, use + the hg parents + command. + + &interaction.tour.parents; + + If you look back at , + you'll see arrows connecting each changeset. The node that + the arrow leads from in each case is a + parent, and the node that the arrow leads + 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 hg update command. + + &interaction.tour.older; + + If you omit an explicit revision, hg update will update to the tip + revision, as shown by the second call to hg update in the example + above. + + + + 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 hg + pull above, we'll create a temporary repository + to push our changes into. + + &interaction.tour.clone-push; + + The hg outgoing command + tells us what changes would be pushed into another + repository. + + &interaction.tour.outgoing; + + And the + hg push command does the + actual push. + + &interaction.tour.push; + + As with hg + pull, the hg push + command does not update the working directory in the + repository that it's pushing changes into. Unlike hg pull, hg + push does not provide a -u + option that updates the other repository's working directory. + This asymmetry is deliberate: the repository we're pushing to + might be on a remote server and shared between several people. + If we were to update its working directory while someone was + working in it, their work would be disrupted. + + 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; + + + 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; + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch02-tour-merge.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch02-tour-merge.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,401 @@ + + + + + A tour of Mercurial: merging work + + 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 merging + changes from separate repositories. + + + Merging streams of work + + Merging is a fundamental part of working with a distributed + revision control tool. + + 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. + + 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. + + + 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 + hello.c with different contents. The + histories of the two repositories have also diverged, as + illustrated in . + + &interaction.tour.merge.cat; + +
    + Divergent recent histories of the <filename + class="directory">my-hello</filename> and <filename + class="directory">my-new-hello</filename> + repositories + + + XXX add text + +
    + + We already know that pulling changes from our my-hello repository will have no + effect on the working directory. + + &interaction.tour.merge.pull; + + However, the hg pull + command says something about heads. + + + 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. + +
    + Repository contents after pulling from <filename + class="directory">my-hello</filename> into <filename + class="directory">my-new-hello</filename> + + + + + XXX add text + +
    + + In , you can + see the effect of the pull from my-hello into my-new-hello. The history that + was already present in my-new-hello is untouched, but + a new revision has been added. By referring to , we can see that the + changeset ID remains the same in the new + repository, but the 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 hg heads command. + + &interaction.tour.merge.heads; + +
    + + Performing the merge + + What happens if we try to use the normal hg update command to update to the + new tip? + + &interaction.tour.merge.update; + + Mercurial is telling us that the hg + update command won't do a merge; it won't update + the working directory when it thinks we might want to do + a merge, unless we force it to do so. Instead, we use the + hg merge command to merge the + two heads. + + &interaction.tour.merge.merge; + + This updates the working directory so that it contains + changes from both heads, which is + reflected in both the output of hg + parents and the contents of + hello.c. + + &interaction.tour.merge.parents; + + + + Committing the results of the merge + + Whenever we've done a merge, hg + parents will display two parents until we hg commit the results of the + merge. + + &interaction.tour.merge.commit; + + We now have a new tip revision; notice that it has + both of our former heads as its parents. + These are the same revisions that were previously displayed by + hg parents. + + &interaction.tour.merge.tip; + + In , 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. + +
    + Working directory and repository during merge, and + following commit + + + + + XXX add text + +
    + + We sometimes talk about a merge having + sides: the left side is the first parent + in the output of hg parents, + and the right side is the second. If the working directory + was at e.g. revision 5 before we began a merge, that revision + will become the left side of the merge. +
    +
    + + + Merging conflicting changes + + Most merges are simple affairs, but sometimes you'll find + yourself merging changes where each side modifies the same portions + of the same files. Unless both modifications are identical, + this results in a conflict, where you have + to decide how to reconcile the different changes into something + coherent. + +
    + Conflicting changes to a document + + + XXX add text + +
    + + 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, usually one + that displays some kind of graphical conflict resolution + interface. By default, Mercurial tries 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, it tries a few + different graphical merging tools. + + It's also possible to get Mercurial to run another program + or script instead of hgmerge, by setting the + HGMERGE environment variable to the name of your + preferred program. + + + Using a graphical merge tool + + My preferred graphical merge tool is + kdiff3, which I'll use to describe the + features that are common to graphical file merging tools. You + can see a screenshot of kdiff3 in action in + . The kind of + merge it is performing is called a 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: + + At the left is the base + version of the file, i.e. the most recent version from + which the two versions we're trying to merge are + descended. + + In the middle is our version of + the file, with the contents that we modified. + + On the right is their version + of the file, the one that from the changeset that we're + trying to merge with. + + In the pane below these is the current + 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 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. + +
    + Using <command>kdiff3</command> to merge versions of a + file + + + + + XXX add text + + +
    + + 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 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). + +
    + + A worked example + + In this example, we will reproduce the file modification + history of + 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'll set + HGMERGE to tell Mercurial to use the + non-interactive merge command. This is + bundled with many Unix-like systems. (If you're following this + example on your computer, don't bother setting + HGMERGE.) + + &interaction.tour-merge-conflict.merge; + + Because merge can't resolve the + conflicting changes, it leaves 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 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; + + +
    + + Simplifying the pull-merge-commit sequence + + The process of merging changes as outlined above is + straightforward, but requires running three commands in + sequence. + hg pull -u +hg merge +hg commit -m 'Merged remote changes' + 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 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 fetch + extension adds a new command called, not surprisingly, hg fetch. This extension acts as a + combination of hg pull -u, + hg merge and hg commit. 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 (if the merge succeeded) 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 fetch extension is easy. Edit the + .hgrc file in your home + directory, and either go to the extensions section or create an + extensions section. Then + add a line that simply reads + fetch=. + + [extensions] +fetch = + + (Normally, the right-hand side of the + = would indicate where to find + the extension, but since the fetch extension is in the standard + distribution, Mercurial knows where to search for it.) + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch03-concepts.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch03-concepts.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,754 @@ + + + + + Behind the scenes + + 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 these details + certainly isn't necessary, so it is certainly safe to skip this + chapter. However, I think you will get more out of the software + with a mental model of what's going on. + + Being able to understand what's going on behind the + scenes gives me confidence that Mercurial has been carefully + designed to be both safe and + efficient. And just as importantly, if it's + easy for me to retain a good idea of what the software is doing + when I perform a revision control task, I'm less likely to be + surprised by its behavior. + + In this chapter, we'll initially cover the core concepts + behind Mercurial's design, then continue to discuss some of the + interesting details of its implementation. + + + Mercurial's historical record + + + Tracking the history of a single file + + When Mercurial tracks modifications to a file, it stores + the history of that file in a metadata object called a + filelog. Each entry in the filelog + contains enough information to reconstruct one revision of the + file that is being tracked. Filelogs are stored as files in + the .hg/store/data directory. A + filelog contains two kinds of information: revision data, and + an index to help Mercurial to find a revision + efficiently. + + A file that is large, or has a lot of history, has its + filelog stored in separate data + (.d suffix) and index + (.i suffix) files. For + small files without much history, the revision data and index + are combined in a single .i + file. The correspondence between a file in the working + directory and the filelog that tracks its history in the + repository is illustrated in . + +
    + Relationships between files in working directory and + filelogs in repository + + + XXX add text + +
    + +
    + + Managing tracked files + + Mercurial uses a structure called a + manifest to collect together information + about the files that it tracks. Each entry in the manifest + contains information about the files present in a single + changeset. An entry records which files are present in the + changeset, the revision of each file, and a few other pieces + of file metadata. + + + + Recording changeset information + + The changelog contains information + about each changeset. Each revision records who committed a + change, the changeset comment, other pieces of + changeset-related information, and the revision of the + manifest to use. + + + + Relationships between revisions + + Within a changelog, a manifest, or a filelog, each + revision stores a pointer to its immediate parent (or to its + two parents, if it's a merge revision). As I mentioned above, + there are also relationships between revisions + across these structures, and they are + hierarchical in nature. + + For every changeset in a repository, there is exactly one + revision stored in the changelog. Each revision of the + changelog contains a pointer to a single revision of the + manifest. A revision of the manifest stores a pointer to a + single revision of each filelog tracked when that changeset + was created. These relationships are illustrated in + . + +
    + Metadata relationships + + + XXX add text + +
    + + As the illustration shows, there is + not a one to one + relationship between revisions in the changelog, manifest, or + filelog. If the manifest hasn't changed between two + changesets, the changelog entries for those changesets will + point to the same revision of the manifest. If a file that + Mercurial tracks hasn't changed between two changesets, the + entry for that file in the two revisions of the manifest will + point to the same revision of its filelog. + +
    +
    + + Safe, efficient storage + + The underpinnings of changelogs, manifests, and filelogs are + provided by a single structure called the + revlog. + + + Efficient storage + + The revlog provides efficient storage of revisions using a + delta mechanism. Instead of storing a + complete copy of a file for each revision, it stores the + changes needed to transform an older revision into the new + revision. For many kinds of file data, these deltas are + typically a fraction of a percent of the size of a full copy + of a file. + + Some obsolete revision control systems can only work with + deltas of text files. They must either store binary files as + complete snapshots or encoded into a text representation, both + of which are wasteful approaches. Mercurial can efficiently + handle deltas of files with arbitrary binary contents; it + doesn't need to treat text as special. + + + + Safe operation + + Mercurial only ever appends data to + the end of a revlog file. It never modifies a section of a + file after it has written it. This is both more robust and + efficient than schemes that need to modify or rewrite + data. + + In addition, Mercurial treats every write as part of a + transaction that can span a number of + files. A transaction is atomic: either + the entire transaction succeeds and its effects are all + visible to readers in one go, or the whole thing is undone. + This guarantee of atomicity means that if you're running two + copies of Mercurial, where one is reading data and one is + writing it, the reader will never see a partially written + result that might confuse it. + + The fact that Mercurial only appends to files makes it + easier to provide this transactional guarantee. The easier it + is to do stuff like this, the more confident you should be + that it's done correctly. + + + + Fast retrieval + + Mercurial cleverly avoids a pitfall common to all earlier + revision control systems: the problem of inefficient + retrieval. Most revision control systems store + the contents of a revision as an incremental series of + modifications against a snapshot. To + reconstruct a specific revision, you must first read the + snapshot, and then every one of the revisions between the + snapshot and your target revision. The more history that a + file accumulates, the more revisions you must read, hence the + longer it takes to reconstruct a particular revision. + +
    + Snapshot of a revlog, with incremental deltas + + + XXX add text + +
    + + The innovation that Mercurial applies to this problem is + simple but effective. Once the cumulative amount of delta + information stored since the last snapshot exceeds a fixed + threshold, it stores a new snapshot (compressed, of course), + instead of another delta. This makes it possible to + reconstruct any revision of a file + quickly. This approach works so well that it has since been + copied by several other revision control systems. + + illustrates + the idea. In an entry in a revlog's index file, Mercurial + stores the range of entries from the data file that it must + read to reconstruct a particular revision. + + + Aside: the influence of video compression + + If you're familiar with video compression or have ever + watched a TV feed through a digital cable or satellite + service, you may know that most video compression schemes + store each frame of video as a delta against its predecessor + frame. In addition, these schemes use lossy + compression techniques to increase the compression ratio, so + visual errors accumulate over the course of a number of + inter-frame deltas. + + Because it's possible for a video stream to drop + out occasionally due to signal glitches, and to + limit the accumulation of artefacts introduced by the lossy + compression process, video encoders periodically insert a + complete frame (called a key frame) into the + video stream; the next delta is generated against that + frame. This means that if the video signal gets + interrupted, it will resume once the next key frame is + received. Also, the accumulation of encoding errors + restarts anew with each key frame. + + +
    + + Identification and strong integrity + + Along with delta or snapshot information, a revlog entry + contains a cryptographic hash of the data that it represents. + This makes it difficult to forge the contents of a revision, + and easy to detect accidental corruption. + + Hashes provide more than a mere check against corruption; + they are used as the identifiers for revisions. The changeset + identification hashes that you see as an end user are from + revisions of the changelog. Although filelogs and the + manifest also use hashes, Mercurial only uses these behind the + scenes. + + Mercurial verifies that hashes are correct when it + retrieves file revisions and when it pulls changes from + another repository. If it encounters an integrity problem, it + will complain and stop whatever it's doing. + + In addition to the effect it has on retrieval efficiency, + Mercurial's use of periodic snapshots makes it more robust + against partial data corruption. If a revlog becomes partly + corrupted due to a hardware error or system bug, it's often + possible to reconstruct some or most revisions from the + uncorrupted sections of the revlog, both before and after the + corrupted section. This would not be possible with a + delta-only storage model. + + +
    + + Revision history, branching, and merging + + Every entry in a Mercurial revlog knows the identity of its + immediate ancestor revision, usually referred to as its + parent. In fact, a revision contains room + for not one parent, but two. Mercurial uses a special hash, + called the null ID, to represent the idea + there is no parent here. This hash is simply a + string of zeroes. + + In , 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. + +
    + The conceptual structure of a revlog + + + XXX add text + +
    + +
    + + The working directory + + In the working directory, Mercurial stores a snapshot of the + files from the repository as of a particular changeset. + + The working directory knows which changeset + it contains. When you update the working directory to contain a + particular changeset, Mercurial looks up the appropriate + revision of the manifest to find out which files it was tracking + at the time that changeset was committed, and which revision of + each file was then current. It then recreates a copy of each of + those files, with the same contents it had when the changeset + was committed. + + The dirstate contains Mercurial's + knowledge of the working directory. This details which + changeset the working directory is updated to, and all of the + files that Mercurial is tracking in the working + directory. + + Just as a revision of a revlog has room for two parents, so + that it can represent either a normal revision (with one parent) + or a merge of two earlier revisions, the dirstate has slots for + two parents. When you use the hg + update command, the changeset that you update to is + stored in the first parent slot, and the null ID + in the second. When you hg + merge with another changeset, the first parent + remains unchanged, and the second parent is filled in with the + changeset you're merging with. The hg + parents command tells you what the parents of the + dirstate are. + + + What happens when you commit + + The dirstate stores parent information for more than just + book-keeping purposes. Mercurial uses the parents of the + dirstate as the parents of a new + changeset when you perform a commit. + +
    + The working directory can have two parents + + + XXX add text + +
    + + shows the + normal state of the working directory, where it has a single + changeset as parent. That changeset is the + tip, the newest changeset in the + repository that has no children. + +
    + The working directory gains new parents after a + commit + + + XXX add text + +
    + + 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 . Mercurial + doesn't touch any of the files in the working directory when + you commit; it just modifies the dirstate to note its new + parents. + +
    + + Creating a new head + + It's perfectly normal to update the working directory to a + changeset other than the current tip. For example, you might + want to know what your project looked like last Tuesday, or + you could be looking through changesets to see which one + introduced a bug. In cases like this, the natural thing to do + is update the working directory to the changeset you're + interested in, and then examine the files in the working + directory directly to see their contents as they were when you + committed that changeset. The effect of this is shown in + . + +
    + The working directory, updated to an older + changeset + + + XXX add text + +
    + + Having updated the working directory to an + older changeset, what happens if you make some changes, and + then commit? Mercurial behaves in the same way as I outlined + above. The parents of the working directory become the + parents of the new changeset. This new changeset has no + children, so it becomes the new tip. And the repository now + contains two changesets that have no children; we call these + heads. You can see the structure that + this creates in . + +
    + After a commit made while synced to an older + changeset + + + XXX add text + +
    + + + If you're new to Mercurial, you should keep in mind a + common error, which is to use the hg pull command without any + options. By default, the hg + pull command does not + update the working directory, so you'll bring new changesets + into your repository, but the working directory will stay + synced at the same changeset as before the pull. If you + make some changes and commit afterwards, you'll thus create + a new head, because your working directory isn't synced to + whatever the current tip is. + + I put the word error in + quotes because all that you need to do to rectify this + situation is hg merge, then + hg commit. In other words, + this almost never has negative consequences; it's just + something of a surprise for newcomers. I'll discuss other + ways to avoid this behavior, and why Mercurial behaves in + this initially surprising way, later on. + + +
    + + Merging changes + + When you run the hg + merge command, Mercurial leaves the first parent + of the working directory unchanged, and sets the second parent + to the changeset you're merging with, as shown in . + +
    + Merging two heads + + + + + XXX add text + +
    + + Mercurial also has to modify the working directory, to + merge the files managed in the two changesets. Simplified a + little, the merging process goes like this, for every file in + the manifests of both changesets. + + If neither changeset has modified a file, do + nothing with that file. + + If one changeset has modified a file, and the + other hasn't, create the modified copy of the file in the + working directory. + + If one changeset has removed a file, and the + other hasn't (or has also deleted it), delete the file + from the working directory. + + If one changeset has removed a file, but the + other has modified the file, ask the user what to do: keep + the modified file, or remove it? + + If both changesets have modified a file, + invoke an external merge program to choose the new + contents for the merged file. This may require input from + the user. + + If one changeset has modified a file, and the + other has renamed or copied the file, make sure that the + changes follow the new name of the file. + + There are more details&emdash;merging has plenty of corner + cases&emdash;but these are the most common choices that are + involved in a merge. As you can see, most cases are + completely automatic, and indeed most merges finish + automatically, without requiring your input to resolve any + conflicts. + + When you're thinking about what happens when you commit + after a merge, once again the working directory is the + changeset I'm about to commit. After the hg merge command completes, the + working directory has two parents; these will become the + parents of the new changeset. + + Mercurial lets you perform multiple merges, but you must + commit the results of each individual merge as you go. This + is necessary because Mercurial only tracks two parents for + both revisions and the working directory. While it would be + technically possible to merge multiple changesets at once, the + prospect of user confusion and making a terrible mess of a + merge immediately becomes overwhelming. + +
    + + + Merging and renames + + A surprising number of revision control systems pay little + or no attention to a file's name over + time. For instance, it used to be common that if a file got + renamed on one side of a merge, the changes from the other + side would be silently dropped. + + Mercurial records metadata when you tell it to perform a + rename or copy. It uses this metadata during a merge to do the + right thing in the case of a merge. For instance, if I rename + a file, and you edit it without renaming it, when we merge our + work the file will be renamed and have your edits + applied. + +
    + + + Other interesting design features + + In the sections above, I've tried to highlight some of the + most important aspects of Mercurial's design, to illustrate that + it pays careful attention to reliability and performance. + However, the attention to detail doesn't stop there. There are + a number of other aspects of Mercurial's construction that I + personally find interesting. I'll detail a few of them here, + separate from the big ticket items above, so that + if you're interested, you can gain a better idea of the amount + of thinking that goes into a well-designed system. + + + Clever compression + + When appropriate, Mercurial will store both snapshots and + deltas in compressed form. It does this by always + trying to compress a snapshot or delta, + but only storing the compressed version if it's smaller than + the uncompressed version. + + This means that Mercurial does the right + thing when storing a file whose native form is + compressed, such as a zip archive or a JPEG + image. When these types of files are compressed a second + time, the resulting file is usually bigger than the + once-compressed form, and so Mercurial will store the plain + zip or JPEG. + + Deltas between revisions of a compressed file are usually + larger than snapshots of the file, and Mercurial again does + the right thing in these cases. It finds that + such a delta exceeds the threshold at which it should store a + complete snapshot of the file, so it stores the snapshot, + again saving space compared to a naive delta-only + approach. + + + Network recompression + + When storing revisions on disk, Mercurial uses the + deflate compression algorithm (the same one + used by the popular zip archive format), + which balances good speed with a respectable compression + ratio. However, when transmitting revision data over a + network connection, Mercurial uncompresses the compressed + revision data. + + If the connection is over HTTP, Mercurial recompresses + the entire stream of data using a compression algorithm that + gives a better compression ratio (the Burrows-Wheeler + algorithm from the widely used bzip2 + compression package). This combination of algorithm and + compression of the entire stream (instead of a revision at a + time) substantially reduces the number of bytes to be + transferred, yielding better network performance over most + kinds of network. + + (If the connection is over ssh, + Mercurial doesn't recompress the + stream, because ssh can already do this + itself.) + + + + + Read/write ordering and atomicity + + Appending to files isn't the whole story when + it comes to guaranteeing that a reader won't see a partial + write. If you recall , + revisions in + the changelog point to revisions in the manifest, and + revisions in the manifest point to revisions in filelogs. + This hierarchy is deliberate. + + A writer starts a transaction by writing filelog and + manifest data, and doesn't write any changelog data until + those are finished. A reader starts by reading changelog + data, then manifest data, followed by filelog data. + + Since the writer has always finished writing filelog and + manifest data before it writes to the changelog, a reader will + never read a pointer to a partially written manifest revision + from the changelog, and it will never read a pointer to a + partially written filelog revision from the manifest. + + + + Concurrent access + + The read/write ordering and atomicity guarantees mean that + Mercurial never needs to lock a + repository when it's reading data, even if the repository is + being written to while the read is occurring. This has a big + effect on scalability; you can have an arbitrary number of + Mercurial processes safely reading data from a repository + safely all at once, no matter whether it's being written to or + not. + + The lockless nature of reading means that if you're + sharing a repository on a multi-user system, you don't need to + grant other local users permission to + write to your repository in order for + them to be able to clone it or pull changes from it; they only + need read permission. (This is + not a common feature among revision + control systems, so don't take it for granted! Most require + readers to be able to lock a repository to access it safely, + and this requires write permission on at least one directory, + which of course makes for all kinds of nasty and annoying + security and administrative problems.) + + Mercurial uses locks to ensure that only one process can + write to a repository at a time (the locking mechanism is safe + even over filesystems that are notoriously hostile to locking, + such as NFS). If a repository is locked, a writer will wait + for a while to retry if the repository becomes unlocked, but + if the repository remains locked for too long, the process + attempting to write will time out after a while. This means + that your daily automated scripts won't get stuck forever and + pile up if a system crashes unnoticed, for example. (Yes, the + timeout is configurable, from zero to infinity.) + + + Safe dirstate access + + As with revision data, Mercurial doesn't take a lock to + read the dirstate file; it does acquire a lock to write it. + To avoid the possibility of reading a partially written copy + of the dirstate file, Mercurial writes to a file with a + unique name in the same directory as the dirstate file, then + renames the temporary file atomically to + dirstate. The file named + dirstate is thus guaranteed to be + complete, not partially written. + + + + + Avoiding seeks + + Critical to Mercurial's performance is the avoidance of + seeks of the disk head, since any seek is far more expensive + than even a comparatively large read operation. + + This is why, for example, the dirstate is stored in a + single file. If there were a dirstate file per directory that + Mercurial tracked, the disk would seek once per directory. + Instead, Mercurial reads the entire single dirstate file in + one step. + + Mercurial also uses a copy on write scheme + when cloning a repository on local storage. Instead of + copying every revlog file from the old repository into the new + repository, it makes a hard link, which is a + shorthand way to say these two names point to the same + file. When Mercurial is about to write to one of a + revlog's files, it checks to see if the number of names + pointing at the file is greater than one. If it is, more than + one repository is using the file, so Mercurial makes a new + copy of the file that is private to this repository. + + A few revision control developers have pointed out that + this idea of making a complete private copy of a file is not + very efficient in its use of storage. While this is true, + storage is cheap, and this method gives the highest + performance while deferring most book-keeping to the operating + system. An alternative scheme would most likely reduce + performance and increase the complexity of the software, each + of which is much more important to the feel of + day-to-day use. + + + + Other contents of the dirstate + + Because Mercurial doesn't force you to tell it when you're + modifying a file, it uses the dirstate to store some extra + information so it can determine efficiently whether you have + modified a file. For each file in the working directory, it + stores the time that it last modified the file itself, and the + size of the file at that time. + + When you explicitly hg + add, hg remove, + hg rename or hg copy files, Mercurial updates the + dirstate so that it knows what to do with those files when you + commit. + + When Mercurial is checking the states of files in the + working directory, it first checks a file's modification time. + If that has not changed, the file must not have been modified. + If the file's size has changed, the file must have been + modified. If the modification time has changed, but the size + has not, only then does Mercurial need to read the actual + contents of the file to see if they've changed. Storing these + few extra pieces of information dramatically reduces the + amount of data that Mercurial needs to read, which yields + large performance improvements compared to other revision + control systems. + + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch04-daily.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch04-daily.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,682 @@ + + + + + Mercurial in daily use + + + Telling Mercurial which files to track + + Mercurial does not work with files in your repository unless + you tell it to manage them. The hg + status command will tell you which files Mercurial + doesn't know about; it uses a + ? to display such + files. + + To tell Mercurial to track a file, use the hg add command. Once you have added a + file, the entry in the output of hg + status for that file changes from + ? to + A. + + &interaction.daily.files.add; + + After you run a hg commit, + the files that you added before the commit will no longer be + listed in the output of hg + status. The reason for this is that by default, hg status only tells you about + interesting files&emdash;those that you have (for + example) modified, removed, or renamed. If you have a repository + that contains thousands of files, you will rarely want to know + about files that Mercurial is tracking, but that have not + changed. (You can still get this information; we'll return to + this later.) + + Once you add a file, Mercurial doesn't do anything with it + immediately. Instead, it will take a snapshot of the file's + state the next time you perform a commit. It will then continue + to track the changes you make to the file every time you commit, + until you remove the file. + + + Explicit versus implicit file naming + + A useful behavior 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 myfile.txt 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. The assumption + that Mercurial makes in such cases is that we know what we + are doing, and it doesn't print any output. + + However, when we imply the names of + files by giving the name of a directory, Mercurial takes the + extra step of printing the name of each file that it does + something with. This makes it more clear what is happening, + and reduces the likelihood of a silent and nasty surprise. + This behavior is common to most Mercurial commands. + + + + Mercurial tracks files, not directories + + Mercurial does not track directory information. Instead, + it tracks the path to a file. Before creating a file, it + first creates any missing directory components of the path. + After it deletes a file, it then deletes any empty directories + that were in the deleted file's path. This sounds like a + trivial distinction, but it has one minor practical + consequence: it is not possible to represent a completely + empty directory in Mercurial. + + Empty directories are rarely useful, and there are + unintrusive workarounds that you can use to achieve an + appropriate effect. The developers of Mercurial thus felt + that the complexity that would be required to manage empty + directories was not worth the limited benefit this feature + would bring. + + If you need an empty directory in your repository, there + are a few ways to achieve this. One is to create a directory, + then hg add a + hidden file to that directory. On Unix-like + systems, any file name that begins with a period + (.) is treated as hidden by + most commands and GUI tools. This approach is illustrated + below. + +&interaction.daily.files.hidden; + + Another way to tackle a need for an empty directory is to + simply create one in your automated build scripts before they + will need it. + + + + + How to stop tracking a file + + Once you decide that a file no longer belongs in your + repository, use the hg remove + command. This deletes the file, and tells Mercurial to stop + tracking it. A removed file is represented in the output of + hg status with a + R. + + &interaction.daily.files.remove; + + After you hg remove a file, + Mercurial will no longer track changes to that file, even if you + recreate a file with the same name in your working directory. + If you do recreate a file with the same name and want Mercurial + to track the new file, simply hg + add it. Mercurial will know that the newly added + file is not related to the old file of the same name. + + + Removing a file does not affect its history + + It is important to understand that removing a file has + only two effects. + + It removes the current version of the file + from the working directory. + + It stops Mercurial from tracking changes to + the file, from the time of the next commit. + + Removing a file does not in any way + alter the history of the file. + + If you update the working directory to a + changeset that was committed when it was still tracking a file + that you later removed, the file will reappear in the working + directory, with the contents it had when you committed that + changeset. If you then update the working directory to a + later changeset, in which the file had been removed, Mercurial + will once again remove the file from the working + directory. + + + + Missing files + + Mercurial considers a file that you have deleted, but not + used hg remove to delete, to + be missing. A missing file is + represented with ! in the + output of hg status. + Mercurial commands will not generally do anything with missing + files. + + &interaction.daily.files.missing; + + If your repository contains a file that hg status reports as missing, and + you want the file to stay gone, you can run hg remove at any + time later on, to tell Mercurial that you really did mean to + remove the file. + + &interaction.daily.files.remove-after; + + On the other hand, if you deleted the missing file by + accident, give hg revert the + name of the file to recover. It will reappear, in unmodified + form. + + &interaction.daily.files.recover-missing; + + + + Aside: why tell Mercurial explicitly to remove a + file? + + You might wonder why Mercurial requires you to explicitly + tell it that you are deleting a file. Early during the + development of Mercurial, it let you delete a file however you + pleased; Mercurial would notice the absence of the file + automatically when you next ran a hg + commit, and stop tracking the file. In practice, + this made it too easy to accidentally remove a file without + noticing. + + + + Useful shorthand&emdash;adding and removing files in one + step + + Mercurial offers a combination command, hg addremove, that adds untracked + files and marks missing files as removed. + + &interaction.daily.files.addremove; + + The hg commit command + also provides a + option that performs this same add-and-remove, immediately + followed by a commit. + + &interaction.daily.files.commit-addremove; + + + + + Copying files + + Mercurial provides a hg + copy command that lets you make a new copy of a + file. When you copy a file using this command, Mercurial makes + a record of the fact that the new file is a copy of the original + file. It treats these copied files specially when you merge + your work with someone else's. + + + The results of copying during a merge + + What happens during a merge is that changes + follow a copy. To best illustrate what this + means, let's create an example. We'll start with the usual + tiny repository that contains a single file. + + &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 hg copy command to make a copy of + the first file we created. + + &interaction.daily.copy.copy; + + If we look at the output of the hg + status command afterwards, the copied file looks + just like a normal added file. + + &interaction.daily.copy.status; + + But if we pass the option to hg status, it prints another line of + output: this is the file that our newly-added file was copied + from. + + &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 file in this + repository. When we pull the changes from the first + repository, and merge the two heads, Mercurial will propagate + the changes that we made locally to file + into its copy, new-file. + + &interaction.daily.copy.merge; + + + + Why should changes follow copies? + + This behavior&emdash;of changes to a file + propagating out to copies of the file&emdash;might seem + esoteric, but in most cases it's highly desirable. + + First of all, remember that this propagation + only happens when you merge. So if you + hg copy a file, and + subsequently modify the original file during the normal course + of your work, nothing will happen. + + The second thing to know is that modifications will only + propagate across a copy as long as the changeset that you're + merging changes from hasn't yet seen + the copy. + + The reason that Mercurial does this is as follows. Let's + say I make an important bug fix in a source file, and commit + my changes. Meanwhile, you've decided to hg copy the file in your repository, + without knowing about the bug or having seen the fix, and you + have started hacking on your copy of the file. + + If you pulled and merged my changes, and Mercurial + didn't propagate changes across copies, + your new source file would now contain the bug, and unless you + knew to propagate the bug fix by hand, the bug would + remain in your copy of the file. + + By automatically propagating the change that fixed the bug + from the original file to the copy, Mercurial prevents this + class of problem. To my knowledge, Mercurial is the + only revision control system that + propagates changes across copies like this. + + Once your change history has a record that the copy and + subsequent merge occurred, there's usually no further need to + propagate changes from the original file to the copied file, + and that's why Mercurial only propagates changes across copies + at the first merge, and not afterwards. + + + + How to make changes <emphasis>not</emphasis> follow a + copy + + If, for some reason, you decide that this business of + automatically propagating changes across copies is not for + you, simply use your system's normal file copy command (on + Unix-like systems, that's cp) to make a + copy of a file, then hg add + the new copy by hand. Before you do so, though, please do + reread , and make + an informed + decision that this behavior is not appropriate to your + specific case. + + + + Behavior of the <command role="hg-cmd">hg copy</command> + command + + When you use the hg copy + command, Mercurial makes a copy of each source file as it + currently stands in the working directory. This means that if + you make some modifications to a file, then hg copy it without first having + committed those changes, the new copy will also contain the + modifications you have made up until that point. (I find this + behavior a little counterintuitive, which is why I mention it + here.) + + The hg copy + command acts similarly to the Unix cp + command (you can use the hg + cp alias if you prefer). We must supply two or + more arguments, of which the last is treated as the + destination, and all others are + sources. + + If you pass hg copy 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 hg remove + command, if you copy a file manually and then want Mercurial + to know that you've copied the file, simply use the option to hg copy. + + &interaction.daily.copy.after; + + + + + Renaming files + + It's rather more common to need to rename a file than to + make a copy of it. The reason I discussed the hg copy command before talking about + renaming files is that Mercurial treats a rename in essentially + the same way as a copy. Therefore, knowing what Mercurial does + when you copy a file tells you what to expect when you rename a + file. + + When you use the hg rename + command, Mercurial makes a copy of each source file, then + deletes it and marks the file as removed. + + &interaction.daily.rename.rename; + + The hg 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 hg + copy, we must use the option to hg status to see that the added file + is really being tracked by Mercurial as a copy of the original, + now removed, file. + + &interaction.daily.rename.status-copy; + + As with hg remove and + hg copy, you can tell Mercurial + about a rename after the fact using the option. In most other + respects, the behavior of the hg + rename command, and the options it accepts, are + similar to the hg copy + command. + + If you're familiar with the Unix command line, you'll be + glad to know that hg rename + command can be invoked as hg + mv. + + + Renaming files and merging changes + + Since Mercurial's rename is implemented as + copy-and-remove, the same propagation of changes happens when + you merge after a rename as after a copy. + + If I modify a file, and you rename it to a new name, and + then we merge our respective changes, my modifications to the + file under its original name will be propagated into the file + under its new name. (This is something you might expect to + simply work, but not all revision control + systems actually do this.) + + Whereas having changes follow a copy is a feature where + you can perhaps nod and say yes, that might be + useful, it should be clear that having them follow a + rename is definitely important. Without this facility, it + would simply be too easy for changes to become orphaned when + files are renamed. + + + + Divergent renames and merging + + The case of diverging names occurs when two developers + start with a file&emdash;let's call it + foo&emdash;in their respective + repositories. + + &interaction.rename.divergent.clone; + + Anne renames the file to bar. + + &interaction.rename.divergent.rename.anne; + + Meanwhile, Bob renames it to + quux. (Remember that hg mv is an alias for hg rename.) + + &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 behavior is that it always preserves + both names when it merges changesets that + contain divergent renames. + + &interaction.rename.divergent.merge; + + Notice that while Mercurial warns about the divergent + renames, it leaves it up to you to do something about the + divergence after the merge. + + + + Convergent renames and merging + + Another kind of rename conflict occurs when two people + choose to rename different source files + to the same destination. In this case, + Mercurial runs its normal merge machinery, and lets you guide + it to a suitable resolution. + + + + Other name-related corner cases + + Mercurial has a longstanding bug in which it fails to + handle a merge where one side has a file with a given name, + while another has a directory with the same name. This is + documented as issue + 29. + + &interaction.issue29.go; + + + + + + Recovering from mistakes + + Mercurial has some useful commands that will help you to + recover from some common mistakes. + + The hg revert command lets + you undo changes that you have made to your working directory. + For example, if you hg add a + file by accident, just run hg + revert with the name of the file you added, and + while the file won't be touched in any way, it won't be tracked + for adding by Mercurial any longer, either. You can also use + hg revert to get rid of + erroneous changes to a file. + + It's good to remember that the hg + revert command is useful for changes that you have + not yet committed. Once you've committed a change, if you + decide it was a mistake, you can still do something about it, + though your options may be more limited. + + For more information about the hg revert command, and details about + how to deal with changes you have already committed, see . + + + + Dealing with tricky merges + + In a complicated or large project, it's not unusual for a + merge of two changesets to result in some headaches. Suppose + there's a big source file that's been extensively edited by each + side of a merge: this is almost inevitably going to result in + conflicts, some of which can take a few tries to sort + out. + + Let's develop a simple case of this and see how to deal with + it. We'll start off with a repository containing one file, and + clone it twice. + + &interaction.ch04-resolve.init; + + In one clone, we'll modify the file in one way. + + &interaction.ch04-resolve.left; + + In another, we'll modify the file differently. + + &interaction.ch04-resolve.right; + + Next, we'll pull each set of changes into our original + repo. + + &interaction.ch04-resolve.pull; + + We expect our repository to now contain two heads. + + &interaction.ch04-resolve.heads; + + Normally, if we run hg + merge at this point, it will drop us into a GUI that + will let us manually resolve the conflicting edits to + myfile.txt. However, to simplify things + for presentation here, we'd like the merge to fail immediately + instead. Here's one way we can do so. + + &interaction.ch04-resolve.export; + + We've told Mercurial's merge machinery to run the command + false (which, as we desire, fails + immediately) if it detects a merge that it can't sort out + automatically. + + If we now fire up hg + merge, it should grind to a halt and report a + failure. + + &interaction.ch04-resolve.merge; + + Even if we don't notice that the merge failed, Mercurial + will prevent us from accidentally committing the result of a + failed merge. + + &interaction.ch04-resolve.cifail; + + When hg commit fails in + this case, it suggests that we use the unfamiliar hg resolve command. As usual, + hg help resolve will print a + helpful synopsis. + + + File resolution states + + When a merge occurs, most files will usually remain + unmodified. For each file where Mercurial has to do + something, it tracks the state of the file. + + + + A resolved file has been + successfully merged, either automatically by Mercurial or + manually with human intervention. + + + An unresolved file was not merged + successfully, and needs more attention. + + + + If Mercurial sees any file in the + unresolved state after a merge, it considers the merge to have + failed. Fortunately, we do not need to restart the entire + merge from scratch. + + The or + option to hg resolve prints out the state of + each merged file. + + &interaction.ch04-resolve.list; + + In the output from hg + resolve, a resolved file is marked with + R, while an unresolved file is marked with + U. If any files are listed with + U, we know that an attempt to commit the + results of the merge will fail. + + + + Resolving a file merge + + We have several options to move a file from the unresolved + into the resolved state. By far the most common is to rerun + hg resolve. If we pass the + names of individual files or directories, it will retry the + merges of any unresolved files present in those locations. We + can also pass the + or option, which + will retry the merges of all unresolved + files. + + Mercurial also lets us modify the resolution state of a + file directly. We can manually mark a file as resolved using + the option, or + as unresolved using the option. This allows + us to clean up a particularly messy merge by hand, and to keep + track of our progress with each file as we go. + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch05-collab.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch05-collab.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1575 @@ + + + + + Collaborating with other people + + As a completely decentralised tool, Mercurial doesn't impose + any policy on how people ought to work with each other. However, + if you're new to distributed revision control, it helps to have + some tools and examples in mind when you're thinking about + possible workflow models. + + + Mercurial's web interface + + Mercurial has a powerful web interface that provides several + useful capabilities. + + For interactive use, the web interface lets you browse a + single repository or a collection of repositories. You can view + the history of a repository, examine each change (comments and + diffs), and view the contents of each directory and file. You + can even get a view of history that gives a graphical view of + the relationships between individual changes and merges. + + Also for human consumption, the web interface provides + Atom and RSS feeds of the changes in a repository. This lets you + subscribe to a repository using your favorite + feed reader, and be automatically notified of activity in that + repository as soon as it happens. I find this capability much + more convenient than the model of subscribing to a mailing list + to which notifications are sent, as it requires no additional + configuration on the part of whoever is serving the + repository. + + The web interface also lets remote users clone a repository, + pull changes from it, and (when the server is configured to + permit it) push changes back to it. Mercurial's HTTP tunneling + protocol aggressively compresses data, so that it works + efficiently even over low-bandwidth network connections. + + The easiest way to get started with the web interface is to + use your web browser to visit an existing repository, such as + the master Mercurial repository at http://www.selenic.com/repo/hg. + + If you're interested in providing a web interface + to your own repositories, there are several good ways to do + this. + + The easiest and fastest way to get started in an informal + environment is to use the hg + serve command, which is best suited to short-term + lightweight serving. See below for details of how to use + this command. + + For longer-lived repositories that you'd like to have + permanently available, there are several public hosting services + available. + + + + Bitbucket, at http://bitbucket.org/, + provides free hosting for open source projects, and paid + hosting for commercial projects. + + + + If you would prefer to host your own repositories, Mercurial + has built-in support for several popular hosting technologies, + most notably CGI (Common Gateway Interface), and WSGI (Web + Services Gateway Interface). See for details of CGI and WSGI + configuration. + + + + Collaboration models + + With a suitably flexible tool, making decisions about + workflow is much more of a social engineering challenge than a + technical one. Mercurial imposes few limitations on how you can + structure the flow of work in a project, so it's up to you and + your group to set up and live with a model that matches your own + particular needs. + + + Factors to keep in mind + + The most important aspect of any model that you must keep + in mind is how well it matches the needs and capabilities of + the people who will be using it. This might seem + self-evident; even so, you still can't afford to forget it for + a moment. + + I once put together a workflow model that seemed to make + perfect sense to me, but that caused a considerable amount of + consternation and strife within my development team. In spite + of my attempts to explain why we needed a complex set of + branches, and how changes ought to flow between them, a few + team members revolted. Even though they were smart people, + they didn't want to pay attention to the constraints we were + operating under, or face the consequences of those constraints + in the details of the model that I was advocating. + + Don't sweep foreseeable social or technical problems under + the rug. Whatever scheme you put into effect, you should plan + for mistakes and problem scenarios. Consider adding automated + machinery to prevent, or quickly recover from, trouble that + you can anticipate. As an example, if you intend to have a + branch with not-for-release changes in it, you'd do well to + think early about the possibility that someone might + accidentally merge those changes into a release branch. You + could avoid this particular problem by writing a hook that + prevents changes from being merged from an inappropriate + branch. + + + + Informal anarchy + + I wouldn't suggest an anything goes + approach as something sustainable, but it's a model that's + easy to grasp, and it works perfectly well in a few unusual + situations. + + As one example, many projects have a loose-knit group of + collaborators who rarely physically meet each other. Some + groups like to overcome the isolation of working at a distance + by organizing 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 or a hacking session in a coffee shop are the perfect places to use the + hg serve command, since + hg serve does not require any + fancy server infrastructure. You can get started with + hg serve in moments, by + reading below. Then simply + tell the person next to you that you're running a server, send + the URL to them in an instant message, and you immediately + have a quick-turnaround way to work together. They can type + your URL into their web browser and quickly review your + changes; or they can pull a bugfix from you and verify it; or + they can clone a branch containing a new feature and try it + out. + + The charm, and the problem, with doing things + in an ad hoc fashion like this is that only people who know + about your changes, and where they are, can see them. Such an + informal approach simply doesn't scale beyond a handful + people, because each individual needs to know about + n different repositories to pull + from. + + + + A single central repository + + For smaller projects migrating from a centralised revision + control tool, perhaps the easiest way to get started is to + have changes flow through a single shared central repository. + This is also the most common building block for + more ambitious workflow schemes. + + Contributors start by cloning a copy of this repository. + They can pull changes from it whenever they need to, and some + (perhaps all) developers have permission to push a change back + when they're ready for other people to see it. + + Under this model, it can still often make sense for people + to pull changes directly from each other, without going + through the central repository. Consider a case in which I + have a tentative bug fix, but I am worried that if I were to + publish it to the central repository, it might subsequently + break everyone else's trees as they pull it. To reduce the + potential for damage, I can ask you to clone my repository + into a temporary repository of your own and test it. This + lets us put off publishing the potentially unsafe change until + it has had a little testing. + + If a team is hosting its own repository in this + kind of scenario, people will usually use the + ssh protocol to securely push changes to + the central repository, as documented in . It's also usual to publish a + read-only copy of the repository over HTTP, as in + . 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. + + + + A hosted central repository + + A wonderful thing about public hosting services like + Bitbucket is that + not only do they handle the fiddly server configuration + details, such as user accounts, authentication, and secure + wire protocols, they provide additional infrastructure to make + this model work well. + + For instance, a well-engineered hosting service will let + people clone their own copies of a repository with a single + click. This lets people work in separate spaces and share + their changes when they're ready. + + In addition, a good hosting service will let people + communicate with each other, for instance to say there + are changes ready for you to review in this + tree. + + + + 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 into the stable branch. + + 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 hg 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 + hg 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, + we can then clone the main branch on the server to a new + stable branch, also on the server. + + &interaction.branching.clone; + + If we need to make a change to the stable + branch, we can then clone that + repository, make our changes, commit, and push our 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 isolated + from each other. The changes that we made on the main branch + don't leak to the stable branch, and vice + versa. + + We'll often want all of our bugfixes on the stable + branch to show up on the main branch, too. Rather than + rewrite a bugfix on the main branch, we can simply pull and + merge changes from the stable to the main branch, and + Mercurial will bring those bugfixes in for us. + + &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, since changes are only + flowing from the stable to the main branch, and not the other + way. + + + + 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. + +
    + Feature branches + + + XXX add text + +
    + + When a particular feature is deemed to be in suitable + shape, someone on that feature team pulls and merges from the + master branch into the feature branch, then pushes back up to + the master branch. +
    + + + The release train + + Some projects are organized on a train + basis: a release is scheduled to happen every few months, and + whatever features are ready when the train is + ready to leave are allowed in. + + This model resembles working with feature branches. The + difference is that when a feature branch misses a train, + someone on the feature team pulls and merges the changes that + went out on that train release into the feature branch, and + the team continues its work on top of that release so that + their feature can make the next release. + + + + The Linux kernel model + + The development of the Linux kernel has a shallow + hierarchical structure, surrounded by a cloud of apparent + chaos. Because most Linux developers use + git, a distributed revision control tool + with capabilities similar to Mercurial, it's useful to + describe the way work flows in that environment; if you like + the ideas, the approach translates well across tools. + + At the center of the community sits Linus Torvalds, the + creator of Linux. He publishes a single source repository + that is considered the authoritative current + tree by the entire developer community. Anyone can clone + Linus's tree, but he is very choosy about whose trees he pulls + from. + + Linus has a number of trusted lieutenants. + As a general rule, he pulls whatever changes they publish, in + most cases without even reviewing those changes. Some of + those lieutenants are generally agreed to be + maintainers, responsible for specific + subsystems within the kernel. If a random kernel hacker wants + to make a change to a subsystem that they want to end up in + Linus's tree, they must find out who the subsystem's + maintainer is, and ask that maintainer to take their change. + If the maintainer reviews their changes and agrees to take + them, they'll pass them along to Linus in due course. + + Individual lieutenants have their own approaches to + reviewing, accepting, and publishing changes; and for deciding + when to feed them to Linus. In addition, there are several + well known branches that people use for different purposes. + For example, a few people maintain stable + repositories of older versions of the kernel, to which they + apply critical fixes as needed. Some maintainers publish + multiple trees: one for experimental changes; one for changes + that they are about to feed upstream; and so on. Others just + publish a single tree. + + This model has two notable features. The first is that + it's pull only. You have to ask, convince, or + beg another developer to take a change from you, because there + are almost no trees to which more than one person can push, + and there's no way to push changes into a tree that someone + else controls. + + The second is that it's based on reputation and acclaim. + If you're an unknown, Linus will probably ignore changes from + you without even responding. But a subsystem maintainer will + probably review them, and will likely take them if they pass + their criteria for suitability. The more good + changes you contribute to a maintainer, the more likely they + are to trust your judgment and accept your changes. If you're + well-known and maintain a long-lived branch for something + Linus hasn't yet accepted, people with similar interests may + pull your changes regularly to keep up with your work. + + Reputation and acclaim don't necessarily cross subsystem + or people boundaries. If you're a respected + but specialised storage hacker, and you try to fix a + networking bug, that change will receive a level of scrutiny + from a network maintainer comparable to a change from a + complete stranger. + + To people who come from more orderly project backgrounds, + the comparatively chaotic Linux kernel development process + often seems completely insane. It's subject to the whims of + individuals; people make sweeping changes whenever they deem + it appropriate; and the pace of development is astounding. + And yet Linux is a highly successful, well-regarded piece of + software. + + + + Pull-only versus shared-push collaboration + + A perpetual source of heat in the open source community is + whether a development model in which people only ever pull + changes from others is better than one in which + multiple people can push changes to a shared + repository. + + Typically, the backers of the shared-push model use tools + that actively enforce this approach. If you're using a + centralised revision control tool such as Subversion, there's + no way to make a choice over which model you'll use: the tool + gives you shared-push, and if you want to do anything else, + you'll have to roll your own approach on top (such as applying + a patch by hand). + + A good distributed revision control tool will + support both models. You and your collaborators can then + structure how you work together based on your own needs and + preferences, not on what contortions your tools force you + into. + + + Where collaboration meets branch management + + Once you and your team set up some shared + repositories and start propagating changes back and forth + between local and shared repos, you begin to face a related, + but slightly different challenge: that of managing the + multiple directions in which your team may be moving at once. + Even though this subject is intimately related to how your + team collaborates, it's dense enough to merit treatment of its + own, in . + +
    + + + The technical side of sharing + + The remainder of this chapter is devoted to the question of + sharing changes with your collaborators. + + + + Informal sharing with <command role="hg-cmd">hg + serve</command> + + Mercurial's hg serve + command is wonderfully suited to small, tight-knit, and + fast-paced group environments. It also provides a great way to + get a feel for using Mercurial commands over a network. + + Run hg serve inside a + repository, and in under a second it will bring up a specialised + HTTP server; this will accept connections from any client, and + serve up data for that repository until you terminate it. + Anyone who knows the URL of the server you just started, and can + talk to your computer over the network, can then use a web + browser or Mercurial to read data from that repository. A URL + for a hg serve instance running + on a laptop is likely to look something like + http://my-laptop.local:8000/. + + The hg serve command is + not a general-purpose web server. It can do + only two things: + + Allow people to browse the history of the + repository it's serving, from their normal web + browsers. + + Speak Mercurial's wire protocol, so that people + can hg clone or hg pull changes from that + repository. + + In particular, hg serve + won't allow remote users to modify your + repository. It's intended for read-only use. + + If you're getting started with Mercurial, there's nothing to + prevent you from using hg serve + to serve up a repository on your own computer, then use commands + like hg clone, hg incoming, and so on to talk to that + server as if the repository was hosted remotely. This can help + you to quickly get acquainted with using commands on + network-hosted repositories. + + + A few things to keep in mind + + Because it provides unauthenticated read access to all + clients, you should only use hg + serve in an environment where you either don't + care, or have complete control over, who can access your + network and pull data from your repository. + + The hg serve command + knows nothing about any firewall software you might have + installed on your system or network. It cannot detect or + control your firewall software. If other people are unable to + talk to a running hg serve + instance, the second thing you should do + (after you make sure that they're using + the correct URL) is check your firewall configuration. + + By default, hg serve + listens for incoming connections on port 8000. If another + process is already listening on the port you want to use, you + can specify a different port to listen on using the option. + + Normally, when hg serve + starts, it prints no output, which can be a bit unnerving. If + you'd like to confirm that it is indeed running correctly, and + find out what URL you should send to your collaborators, start + it with the + option. + + + + + Using the Secure Shell (ssh) protocol + + You can pull and push changes securely over a network + connection using the Secure Shell (ssh) + protocol. To use this successfully, you may have to do a little + bit of configuration on the client or server sides. + + If you're not familiar with ssh, it's the name of + both a command and a network protocol that let you securely + communicate with another computer. To use it with Mercurial, + you'll be setting up one or more user accounts on a server so + that remote users can log in and execute commands. + + (If you are familiar with ssh, you'll + probably find some of the material that follows to be elementary + in nature.) + + + How to read and write ssh URLs + + An ssh URL tends to look like this: + ssh://bos@hg.serpentine.com:22/hg/hgbook + + The ssh:// + part tells Mercurial to use the ssh protocol. + + The bos@ + component indicates what username to log into the server + as. You can leave this out if the remote username is the + same as your local username. + + The + hg.serpentine.com gives + the hostname of the server to log into. + + The :22 identifies the port + number to connect to the server on. The default port is + 22, so you only need to specify a colon and port number if + you're not using port 22. + + The remainder of the URL is the local path to + the repository on the server. + + + There's plenty of scope for confusion with the path + component of ssh URLs, as there is no standard way for tools + to interpret it. Some programs behave differently than others + when dealing with these paths. This isn't an ideal situation, + but it's unlikely to change. Please read the following + paragraphs carefully. + + Mercurial treats the path to a repository on the server as + relative to the remote user's home directory. For example, if + user foo on the server has a home directory + of /home/foo, then an + ssh URL that contains a path component of bar really + refers to the directory /home/foo/bar. + + If you want to specify a path relative to another user's + home directory, you can use a path that starts with a tilde + character followed by the user's name (let's call them + otheruser), like this. + ssh://server/~otheruser/hg/repo + + And if you really want to specify an + absolute path on the server, begin the + path component with two slashes, as in this example. + ssh://server//absolute/path + + + + Finding an ssh client for your system + + Almost every Unix-like system comes with OpenSSH + preinstalled. If you're using such a system, run + which ssh to find out if the + ssh command is installed (it's usually in + /usr/bin). In the + unlikely event that it isn't present, take a look at your + system documentation to figure out how to install it. + + On Windows, the TortoiseHg package is bundled + with a version of Simon Tatham's excellent + plink command, and you should not need to + do any further configuration. + + + + 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. + + + Key pairs are not mandatory + + Mercurial knows nothing about ssh authentication or key + pairs. You can, if you like, safely ignore this section and + the one that follows until you grow tired of repeatedly + typing ssh passwords. + + + + + On a Unix-like system, the + ssh-keygen command will do the + trick. + On Windows, if you're using TortoiseHg, you may need + to download a command named puttygen + from the + PuTTY web site to generate a key pair. See + the + puttygen documentation for + details of how use the command. + + + + When you generate a key pair, it's usually + highly advisable to protect it with a + passphrase. (The only time that you might not want to do this + is when you're using the ssh protocol for automated tasks on a + secure network.) + + Simply generating a key pair isn't enough, however. + You'll need to add the public key to the set of authorised + keys for whatever user you're logging in remotely as. For + servers using OpenSSH (the vast majority), this will mean + adding the public key to a list in a file called authorized_keys in their .ssh + directory. + + On a Unix-like system, your public key will have a + .pub extension. If you're using + puttygen on Windows, you can save the + public key to a file of your choosing, or paste it from the + window it's displayed in straight into the authorized_keys file. + + + Using an authentication agent + + An authentication agent is a daemon that stores + passphrases in memory (so it will forget passphrases if you + log out and log back in again). An ssh client will notice if + it's running, and query it for a passphrase. If there's no + authentication agent running, or the agent doesn't store the + necessary passphrase, you'll have to type your passphrase + every time Mercurial tries to communicate with a server on + your behalf (e.g. whenever you pull or push changes). + + The downside of storing passphrases in an agent is that + it's possible for a well-prepared attacker to recover the + plain text of your passphrases, in some cases even if your + system has been power-cycled. You should make your own + judgment as to whether this is an acceptable risk. It + certainly saves a lot of repeated typing. + + + + On Unix-like systems, the agent is called + ssh-agent, and it's often run + automatically for you when you log in. You'll need to use + the ssh-add command to add passphrases + to the agent's store. + + + On Windows, if you're using TortoiseHg, the + pageant command acts as the agent. As + with puttygen, you'll need to download + pageant from the PuTTY web + site and read its + documentation. The pageant + command adds an icon to your system tray that will let you + manage stored passphrases. + + + + + + Configuring the server side properly + + Because ssh can be fiddly to set up if you're new to it, + a variety of things can go wrong. Add Mercurial + on top, and there's plenty more scope for head-scratching. + Most of these potential problems occur on the server side, not + the client side. The good news is that once you've gotten a + configuration working, it will usually continue to work + indefinitely. + + Before you try using Mercurial to talk to an ssh server, + it's best to make sure that you can use the normal + ssh or putty command to + talk to the server first. If you run into problems with using + these commands directly, Mercurial surely won't work. Worse, + it will obscure the underlying problem. Any time you want to + debug ssh-related Mercurial problems, you should drop back to + making sure that plain ssh client commands work first, + before you worry about whether there's a + problem with Mercurial. + + The first thing to be sure of on the server side is that + you can actually log in from another machine at all. If you + can't use ssh or putty + to log in, the error message you get may give you a few hints + as to what's wrong. The most common problems are as + follows. + + If you get a connection refused + error, either there isn't an SSH daemon running on the + server at all, or it's inaccessible due to firewall + configuration. + + If you get a no route to host + error, you either have an incorrect address for the server + or a seriously locked down firewall that won't admit its + existence at all. + + If you get a permission denied + error, you may have mistyped the username on the server, + or you could have mistyped your key's passphrase or the + remote user's password. + + In summary, if you're having trouble talking to the + server's ssh daemon, first make sure that one is running at + all. On many systems it will be installed, but disabled, by + default. Once you're done with this step, you should then + check that the server's firewall is configured to allow + incoming connections on the port the ssh daemon is listening + on (usually 22). Don't worry about more exotic possibilities + for misconfiguration until you've checked these two + first. + + If you're using an authentication agent on the client side + to store passphrases for your keys, you ought to be able to + log into the server without being prompted for a passphrase or + a password. If you're prompted for a passphrase, there are a + few possible culprits. + + You might have forgotten to use + ssh-add or pageant + to store the passphrase. + + You might have stored the passphrase for the + wrong key. + + If you're being prompted for the remote user's password, + there are another few possible problems to check. + + Either the user's home directory or their + .ssh + directory might have excessively liberal permissions. As + a result, the ssh daemon will not trust or read their + authorized_keys file. + For example, a group-writable home or .ssh + directory will often cause this symptom. + + The user's authorized_keys file may have + a problem. If anyone other than the user owns or can write + to that file, the ssh daemon will not trust or read + it. + + + In the ideal world, you should be able to run the + following command successfully, and it should print exactly + one line of output, the current date and time. + ssh myserver date + + If, on your server, you have login scripts that print + banners or other junk even when running non-interactive + commands like this, you should fix them before you continue, + so that they only print output if they're run interactively. + Otherwise these banners will at least clutter up Mercurial's + output. Worse, they could potentially cause problems with + running Mercurial commands remotely. Mercurial makes tries to + detect and ignore banners in non-interactive + ssh sessions, but it is not foolproof. (If + you're editing your login scripts on your server, the usual + way to see if a login script is running in an interactive + shell is to check the return code from the command + tty -s.) + + Once you've verified that plain old ssh is working with + your server, the next step is to ensure that Mercurial runs on + the server. The following command should run + successfully: + + ssh myserver hg version + + If you see an error message instead of normal hg version output, this is usually + because you haven't installed Mercurial to /usr/bin. Don't worry if this + is the case; you don't need to do that. But you should check + for a few possible problems. + + Is Mercurial really installed on the server at + all? I know this sounds trivial, but it's worth + checking! + + Maybe your shell's search path (usually set + via the PATH environment variable) is + simply misconfigured. + + Perhaps your PATH environment + variable is only being set to point to the location of the + hg executable if the login session is + interactive. This can happen if you're setting the path + in the wrong shell login script. See your shell's + documentation for details. + + The PYTHONPATH environment + variable may need to contain the path to the Mercurial + Python modules. It might not be set at all; it could be + incorrect; or it may be set only if the login is + interactive. + + + If you can run hg version + over an ssh connection, well done! You've got the server and + client sorted out. You should now be able to use Mercurial to + access repositories hosted by that username on that server. + If you run into problems with Mercurial and ssh at this point, + try using the + option to get a clearer picture of what's going on. + + + Using compression with ssh + + Mercurial does not compress data when it uses the ssh + protocol, because the ssh protocol can transparently compress + data. However, the default behavior of ssh clients is + not to request compression. + + Over any network other than a fast LAN (even a wireless + network), using compression is likely to significantly speed + up Mercurial's network operations. For example, over a WAN, + someone measured compression as reducing the amount of time + required to clone a particularly large repository from 51 + minutes to 17 minutes. + + Both ssh and plink + accept a option which + turns on compression. You can easily edit your ~/.hgrc to enable compression for + all of Mercurial's uses of the ssh protocol. Here is how to + do so for regular ssh on Unix-like systems, + for example. + [ui] +ssh = ssh -C + + If you use ssh on a + Unix-like system, you can configure it to always use + compression when talking to your server. To do this, edit + your .ssh/config file + (which may not yet exist), as follows. + + Host hg + Compression yes + HostName hg.example.com + + This defines a hostname alias, + hg. When you use that hostname on the + ssh command line or in a Mercurial + ssh-protocol URL, it will cause + ssh to connect to + hg.example.com and use compression. This + gives you both a shorter name to type and compression, each of + which is a good thing in its own right. + + + + + Serving over HTTP using CGI + + The simplest way to host one or more repositories in a + permanent way is to use a web server and Mercurial's CGI + support. + + 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. + + + High pain tolerance required + + 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. + + If you don't have a strong stomach for tweaking + configurations over and over, or a compelling need to host + your own services, you might want to try one of the public + hosting services that I mentioned earlier. + + + + Web server configuration checklist + + Before you continue, do take a few moments to check a few + aspects of your system's setup. + + + Do you have a web server installed + at all? Mac OS X and some Linux distributions ship with + Apache, but many other systems may not have a web server + installed. + + If you have a web server installed, is it + actually running? On most systems, even if one is + present, it will be disabled by default. + + Is your server configured to allow you to run + CGI programs in the directory where you plan to do so? + Most servers default to explicitly disabling the ability + to run CGI programs. + + + If you don't have a web server installed, and don't have + substantial experience configuring Apache, you should consider + using the lighttpd web server instead of + Apache. Apache has a well-deserved reputation for baroque and + confusing configuration. While lighttpd is + less capable in some ways than Apache, most of these + capabilities are not relevant to serving Mercurial + repositories. And lighttpd is undeniably + much easier to get started with than + Apache. + + + + Basic CGI configuration + + On Unix-like systems, it's common for users to have a + subdirectory named something like public_html in their home + directory, from which they can serve up web pages. A file + named foo in this directory will be + accessible at a URL of the form + http://www.example.com/username/foo. + + To get started, find the hgweb.cgi script that should be + present in your Mercurial installation. If you can't quickly + find a local copy on your system, simply download one from the + master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi. + + You'll need to copy this script into your public_html directory, and + ensure that it's executable. + cp .../hgweb.cgi ~/public_html +chmod 755 ~/public_html/hgweb.cgi + The 755 argument to + chmod is a little more general than just + making the script executable: it ensures that the script is + executable by anyone, and that group and + other write permissions are + not set. If you were to leave those + write permissions enabled, Apache's suexec + subsystem would likely refuse to execute the script. In fact, + suexec also insists that the + directory in which the script resides + must not be writable by others. + chmod 755 ~/public_html + + + What could <emphasis>possibly</emphasis> go + wrong? + + Once you've copied the CGI script into place, go into a + web browser, and try to open the URL http://myhostname/ + myuser/hgweb.cgi, but brace + yourself for instant failure. There's a high probability + that trying to visit this URL will fail, and there are many + possible reasons for this. In fact, you're likely to + stumble over almost every one of the possible errors below, + so please read carefully. The following are all of the + problems I ran into on a system running Fedora 7, with a + fresh installation of Apache, and a user account that I + created specially to perform this exercise. + + Your web server may have per-user directories disabled. + If you're using Apache, search your config file for a + UserDir directive. If there's none + present, per-user directories will be disabled. If one + exists, but its value is disabled, then + per-user directories will be disabled. Otherwise, the + string after UserDir gives the name of + the subdirectory that Apache will look in under your home + directory, for example public_html. + + Your file access permissions may be too restrictive. + The web server must be able to traverse your home directory + and directories under your public_html directory, and + read files under the latter too. Here's a quick recipe to + help you to make your permissions more appropriate. + chmod 755 ~ +find ~/public_html -type d -print0 | xargs -0r chmod 755 +find ~/public_html -type f -print0 | xargs -0r chmod 644 + + The other possibility with permissions is that you might + get a completely empty window when you try to load the + script. In this case, it's likely that your access + permissions are too permissive. Apache's + suexec subsystem won't execute a script + that's group- or world-writable, for example. + + Your web server may be configured to disallow execution + of CGI programs in your per-user web directory. Here's + Apache's default per-user configuration from my Fedora + system. + + &ch06-apache-config.lst; + + If you find a similar-looking + Directory group in your Apache + configuration, the directive to look at inside it is + Options. Add ExecCGI + to the end of this list if it's missing, and restart the web + server. + + If you find that Apache serves you the text of the CGI + script instead of executing it, you may need to either + uncomment (if already present) or add a directive like + this. + AddHandler cgi-script .cgi + + The next possibility is that you might be served with a + colourful Python backtrace claiming that it can't import a + mercurial-related module. This is + actually progress! The server is now capable of executing + your CGI script. This error is only likely to occur if + you're running a private installation of Mercurial, instead + of a system-wide version. Remember that the web server runs + the CGI program without any of the environment variables + that you take for granted in an interactive session. If + this error happens to you, edit your copy of hgweb.cgi and follow the + directions inside it to correctly set your + PYTHONPATH environment variable. + + Finally, you are certain to by + served with another colourful Python backtrace: this one + will complain that it can't find /path/to/repository. Edit + your hgweb.cgi script + and replace the /path/to/repository string + with the complete path to the repository you want to serve + up. + + At this point, when you try to reload the page, you + should be presented with a nice HTML view of your + repository's history. Whew! + + + + Configuring lighttpd + + To be exhaustive in my experiments, I tried configuring + the increasingly popular lighttpd web + server to serve the same repository as I described with + Apache above. I had already overcome all of the problems I + outlined with Apache, many of which are not server-specific. + As a result, I was fairly sure that my file and directory + permissions were good, and that my hgweb.cgi script was properly + edited. + + Once I had Apache running, getting + lighttpd to serve the repository was a + snap (in other words, even if you're trying to use + lighttpd, you should read the Apache + section). I first had to edit the + mod_access section of its config file to + enable mod_cgi and + mod_userdir, both of which were disabled + by default on my system. I then added a few lines to the + end of the config file, to configure these modules. + userdir.path = "public_html" +cgi.assign = (".cgi" => "" ) + With this done, lighttpd ran + immediately for me. If I had configured + lighttpd before Apache, I'd almost + certainly have run into many of the same system-level + configuration problems as I did with Apache. However, I + found lighttpd to be noticeably easier to + configure than Apache, even though I've used Apache for over + a decade, and this was my first exposure to + lighttpd. + + + + + Sharing multiple repositories with one CGI script + + The hgweb.cgi script + only lets you publish a single repository, which is an + annoying restriction. If you want to publish more than one + without wracking yourself with multiple copies of the same + script, each with different names, a better choice is to use + the hgwebdir.cgi + script. + + The procedure to configure hgwebdir.cgi is only a little more + involved than for hgweb.cgi. First, you must obtain + a copy of the script. If you don't have one handy, you can + download a copy from the master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi. + + You'll need to copy this script into your public_html directory, and + ensure that it's executable. + + cp .../hgwebdir.cgi ~/public_html +chmod 755 ~/public_html ~/public_html/hgwebdir.cgi + + With basic configuration out of the way, try to + visit http://myhostname/ + myuser/hgwebdir.cgi in your browser. It should + display an empty list of repositories. If you get a blank + window or error message, try walking through the list of + potential problems in . + + The hgwebdir.cgi + script relies on an external configuration file. By default, + it searches for a file named hgweb.config in the same directory + as itself. You'll need to create this file, and make it + world-readable. The format of the file is similar to a + Windows ini file, as understood by Python's + ConfigParser + web:configparser module. + + The easiest way to configure hgwebdir.cgi is with a section + named collections. This will automatically + publish every repository under the + directories you name. The section should look like + this: + [collections] +/my/root = /my/root + Mercurial interprets this by looking at the directory name + on the right hand side of the + = sign; finding repositories + in that directory hierarchy; and using the text on the + left to strip off matching text from the + names it will actually list in the web interface. The + remaining component of a path after this stripping has + occurred is called a virtual path. + + Given the example above, if we have a repository whose + local path is /my/root/this/repo, the CGI + script will strip the leading /my/root from the name, and + publish the repository with a virtual path of this/repo. If the base URL for + our CGI script is http://myhostname/ + myuser/hgwebdir.cgi, the complete URL for that + repository will be http://myhostname/ + myuser/hgwebdir.cgi/this/repo. + + If we replace /my/root on the left hand side + of this example with /my, then hgwebdir.cgi will only strip off + /my from the repository + name, and will give us a virtual path of root/this/repo instead of + this/repo. + + The hgwebdir.cgi + script will recursively search each directory listed in the + collections section of its configuration + file, but it will not recurse into the + repositories it finds. + + The collections mechanism makes it easy + to publish many repositories in a fire and + forget manner. You only need to set up the CGI + script and configuration file one time. Afterwards, you can + publish or unpublish a repository at any time by simply moving + it into, or out of, the directory hierarchy in which you've + configured hgwebdir.cgi to + look. + + + Explicitly specifying which repositories to + publish + + In addition to the collections + mechanism, the hgwebdir.cgi script allows you + to publish a specific list of repositories. To do so, + create a paths section, with contents of + the following form. + [paths] +repo1 = /my/path/to/some/repo +repo2 = /some/path/to/another + In this case, the virtual path (the component that will + appear in a URL) is on the left hand side of each + definition, while the path to the repository is on the + right. Notice that there does not need to be any + relationship between the virtual path you choose and the + location of a repository in your filesystem. + + If you wish, you can use both the + collections and paths + mechanisms simultaneously in a single configuration + file. + + + Beware duplicate virtual paths + + If several repositories have the same + virtual path, hgwebdir.cgi will not report + an error. Instead, it will behave unpredictably. + + + + + + Downloading source archives + + Mercurial's web interface lets users download an archive + of any revision. This archive will contain a snapshot of the + working directory as of that revision, but it will not contain + a copy of the repository data. + + By default, this feature is not enabled. To enable it, + you'll need to add an allow_archive item to the + web section of your ~/.hgrc; see below for details. + + + Web configuration options + + Mercurial's web interfaces (the hg + serve command, and the hgweb.cgi and hgwebdir.cgi scripts) have a + number of configuration options that you can set. These + belong in a section named web. + + allow_archive: Determines + which (if any) archive download mechanisms Mercurial + supports. If you enable this feature, users of the web + interface will be able to download an archive of whatever + revision of a repository they are viewing. To enable the + archive feature, this item must take the form of a + sequence of words drawn from the list below. + + bz2: A + tar archive, compressed using + bzip2 compression. This has the + best compression ratio, but uses the most CPU time on + the server. + + gz: A + tar archive, compressed using + gzip compression. + + zip: A + zip archive, compressed using LZW + compression. This format has the worst compression + ratio, but is widely used in the Windows world. + + + If you provide an empty list, or don't have an + allow_archive entry at + all, this feature will be disabled. Here is an example of + how to enable all three supported formats. + [web] +allow_archive = bz2 gz zip + + allowpull: + Boolean. Determines whether the web interface allows + remote users to hg pull + and hg clone this + repository over HTTP. If set to no or + false, only the + human-oriented portion of the web interface + is available. + + contact: + String. A free-form (but preferably brief) string + identifying the person or group in charge of the + repository. This often contains the name and email + address of a person or mailing list. It often makes sense + to place this entry in a repository's own .hg/hgrc file, but it can make + sense to use in a global ~/.hgrc if every repository + has a single maintainer. + + maxchanges: + Integer. The default maximum number of changesets to + display in a single page of output. + + maxfiles: + Integer. The default maximum number of modified files to + display in a single page of output. + + stripes: + Integer. If the web interface displays alternating + stripes to make it easier to visually align + rows when you are looking at a table, this number controls + the number of rows in each stripe. + + style: Controls the template + Mercurial uses to display the web interface. Mercurial + ships with several web templates. + + + coal is monochromatic. + + + gitweb emulates the visual + style of git's web interface. + + + monoblue uses solid blues and + greys. + + + paper is the default. + + + spartan was the default for a + long time. + + + You can + also specify a custom template of your own; see + for details. Here, you can + see how to enable the gitweb + style. + [web] +style = gitweb + + templates: + Path. The directory in which to search for template + files. By default, Mercurial searches in the directory in + which it was installed. + + If you are using hgwebdir.cgi, you can place a few + configuration items in a web + section of the hgweb.config file instead of a + ~/.hgrc file, for + convenience. These items are motd and style. + + + Options specific to an individual repository + + A few web configuration + items ought to be placed in a repository's local .hg/hgrc, rather than a user's + or global ~/.hgrc. + + description: String. A + free-form (but preferably brief) string that describes + the contents or purpose of the repository. + + name: + String. The name to use for the repository in the web + interface. This overrides the default name, which is + the last component of the repository's path. + + + + + Options specific to the <command role="hg-cmd">hg + serve</command> command + + Some of the items in the web section of a ~/.hgrc file are only for use + with the hg serve + command. + + accesslog: + Path. The name of a file into which to write an access + log. By default, the hg + serve command writes this information to + standard output, not to a file. Log entries are written + in the standard combined file format used + by almost all web servers. + + address: + String. The local address on which the server should + listen for incoming connections. By default, the server + listens on all addresses. + + errorlog: + Path. The name of a file into which to write an error + log. By default, the hg + serve command writes this information to + standard error, not to a file. + + ipv6: + Boolean. Whether to use the IPv6 protocol. By default, + IPv6 is not used. + + port: + Integer. The TCP port number on which the server should + listen. The default port number used is 8000. + + + + + Choosing the right <filename + role="special">~/.hgrc</filename> file to add <literal + role="rc-web">web</literal> items to + + It is important to remember that a web server like + Apache or lighttpd will run under a user + ID that is different to yours. CGI scripts run by your + server, such as hgweb.cgi, will usually also run + under that user ID. + + If you add web items to + your own personal ~/.hgrc file, CGI scripts won't read that + ~/.hgrc file. Those + settings will thus only affect the behavior of the hg serve command when you run it. + To cause CGI scripts to see your settings, either create a + ~/.hgrc file in the + home directory of the user ID that runs your web server, or + add those settings to a system-wide hgrc file. + + + + + + System-wide configuration + + On Unix-like systems shared by multiple users (such as a + server to which people publish changes), it often makes sense to + set up some global default behaviors, such as what theme to use + in web interfaces. + + If a file named /etc/mercurial/hgrc + exists, Mercurial will read it at startup time and apply any + configuration settings it finds in that file. It will also look + for files ending in a .rc extension in a + directory named /etc/mercurial/hgrc.d, and + apply any configuration settings it finds in each of those + files. + + + Making Mercurial more trusting + + One situation in which a global hgrc + can be useful is if users are pulling changes owned by other + users. By default, Mercurial will not trust most of the + configuration items in a .hg/hgrc file + inside a repository that is owned by a different user. If we + clone or pull changes from such a repository, Mercurial will + print a warning stating that it does not trust their + .hg/hgrc. + + If everyone in a particular Unix group is on the same team + and should trust each other's + configuration settings, or we want to trust particular users, + we can override Mercurial's skeptical defaults by creating a + system-wide hgrc file such as the + following: + + # Save this as e.g. /etc/mercurial/hgrc.d/trust.rc +[trusted] +# Trust all entries in any hgrc file owned by the "editors" or +# "www-data" groups. +groups = editors, www-data + +# Trust entries in hgrc files owned by the following users. +users = apache, bobo + + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch06-filenames.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch06-filenames.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,450 @@ + + + + + File names and pattern matching + + Mercurial provides mechanisms that let you work with file + names in a consistent and expressive way. + + + Simple file naming + + Mercurial uses a unified piece of machinery under the + hood to handle file names. Every command behaves + uniformly with respect to file names. The way in which commands + work with file names is as follows. + + If you explicitly name real files on the command line, + Mercurial works with exactly those files, as you would expect. + &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; + + + + Running commands without any file names + + Mercurial's commands that work with file names have useful + default behaviors when you invoke them without providing any + file names or patterns. What kind of behavior you should + expect depends on what the command does. Here are a few rules + of thumb you can use to predict what a command is likely to do + if you don't give it any names to work with. + + Most commands will operate on the entire working + directory. This is what the hg + add command does, for example. + + If the command has effects that are difficult or + impossible to reverse, it will force you to explicitly + provide at least one name or pattern (see below). This + protects you from accidentally deleting files by running + hg remove with no + arguments, for example. + + + It's easy to work around these default behaviors 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 + .. + + &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 hg + status from a subdirectory, and get it to operate on + the entire working directory while printing file names relative + to our subdirectory, by passing it the output of the hg root command. + + &interaction.filenames.wdir-relname; + + + + Telling you what's going on + + The hg add example in the + preceding section illustrates something else that's helpful + about Mercurial commands. If a command operates on a file that + you didn't name explicitly on the command line, it will usually + print the name of the file, so that you will not be surprised + what's going on. + + The principle here is of least + surprise. If you've exactly named a file on the + command line, there's no point in repeating it back at you. If + Mercurial is acting on a file implicitly, e.g. + because you provided no names, or a directory, or a pattern (see + below), it is safest to tell you what files it's operating on. + + For commands that behave this way, you can silence them + using the option. You + can also get them to print the name of every file, even those + you've named explicitly, using the option. + + + + Using patterns to identify files + + In addition to working with file and directory names, + Mercurial lets you use patterns to identify + files. Mercurial's pattern handling is expressive. + + On Unix-like systems (Linux, MacOS, etc.), the job of + matching file names to patterns normally falls to the shell. On + these systems, you must explicitly tell Mercurial that a name is + a pattern. On Windows, the shell does not expand patterns, so + Mercurial will automatically identify names that are patterns, + and expand them for you. + + To provide a pattern in place of a regular name on the + command line, the mechanism is simple: + syntax:patternbody + That is, a pattern is identified by a short text string that + says what kind of pattern this is, followed by a colon, followed + by the actual pattern. + + Mercurial supports two kinds of pattern syntax. The most + frequently used is called glob; this is the + same kind of pattern matching used by the Unix shell, and should + be familiar to Windows command prompt users, too. + + When Mercurial does automatic pattern matching on Windows, + it uses glob syntax. You can thus omit the + glob: prefix on Windows, but + it's safe to use it, too. + + The re syntax is more powerful; it lets + you specify patterns using regular expressions, also known as + regexps. + + By the way, in the examples that follow, notice that I'm + careful to wrap all of my patterns in quote characters, so that + they won't get expanded by the shell before Mercurial sees + them. + + + Shell-style <literal>glob</literal> patterns + + This is an overview of the kinds of patterns you can use + when you're matching on glob patterns. + + The * character matches + any string, within a single directory. + + &interaction.filenames.glob.star; + + The ** pattern matches + any string, and crosses directory boundaries. It's not a + standard Unix glob token, but it's accepted by several popular + Unix shells, and is very useful. + + &interaction.filenames.glob.starstar; + + The ? pattern matches + any single character. + + &interaction.filenames.glob.question; + + The [ character begins a + character class. This matches any single + character within the class. The class ends with a + ] character. A class may + contain multiple ranges of the form + a-f, which is shorthand for + abcdef. + + &interaction.filenames.glob.range; + + If the first character after the + [ in a character class is a + !, it + negates the class, making it match any + single character not in the class. + + A { begins a group of + subpatterns, where the whole group matches if any subpattern + in the group matches. The , + character separates subpatterns, and + } ends the group. + + &interaction.filenames.glob.group; + + + Watch out! + + Don't forget that if you want to match a pattern in any + directory, you should not be using the + * match-any token, as this + will only match within one directory. Instead, use the + ** token. This small + example illustrates the difference between the two. + + &interaction.filenames.glob.star-starstar; + + + + + Regular expression matching with <literal>re</literal> + patterns + + Mercurial accepts the same regular expression syntax as + the Python programming language (it uses Python's regexp + engine internally). This is based on the Perl language's + regexp syntax, which is the most popular dialect in use (it's + also used in Java, for example). + + I won't discuss Mercurial's regexp dialect in any detail + here, as regexps are not often used. Perl-style regexps are + in any case already exhaustively documented on a multitude of + web sites, and in many books. Instead, I will focus here on a + few things you should know if you find yourself needing to use + regexps with Mercurial. + + A regexp is matched against an entire file name, relative + to the root of the repository. In other words, even if you're + already in subbdirectory foo, if you want to match files + under this directory, your pattern must start with + foo/. + + One thing to note, if you're familiar with Perl-style + regexps, is that Mercurial's are rooted. + That is, a regexp starts matching against the beginning of a + string; it doesn't look for a match anywhere within the + string. To match anywhere in a string, start your pattern + with .*. + + + + + Filtering files + + Not only does Mercurial give you a variety of ways to + specify files; it lets you further winnow those files using + filters. Commands that work with file + names accept two filtering options. + + , or + , lets you + specify a pattern that file names must match in order to be + processed. + + , or + , gives you a + way to avoid processing files, if they + match this pattern. + + You can provide multiple and options on the command line, + and intermix them as you please. Mercurial interprets the + patterns you provide using glob syntax by default (but you can + use regexps if you need to). + + You can read a + filter as process only the files that match this + filter. + + &interaction.filenames.filter.include; + + The filter is best + read as process only the files that don't match this + pattern. + + &interaction.filenames.filter.exclude; + + + + Permanently ignoring unwanted files and directories + + When you create a new repository, the chances are + that over time it will grow to contain files that ought to + not be managed by Mercurial, but which you + don't want to see listed every time you run hg + status. For instance, build products + are files that are created as part of a build but which should + not be managed by a revision control system. The most common + build products are output files produced by software tools such + as compilers. As another example, many text editors litter a + directory with lock files, temporary working files, and backup + files, which it also makes no sense to manage. + + To have Mercurial permanently ignore such files, create a + file named .hgignore in the root of your + repository. You should hg + add this file so that it gets tracked with the rest of + your repository contents, since your collaborators will probably + find it useful too. + + By default, the .hgignore file should + contain a list of regular expressions, one per line. Empty + lines are skipped. Most people prefer to describe the files they + want to ignore using the glob syntax that we + described above, so a typical .hgignore + file will start with this directive: + + syntax: glob + + This tells Mercurial to interpret the lines that follow as + glob patterns, not regular expressions. + + Here is a typical-looking .hgignore + file. + + syntax: glob +# This line is a comment, and will be skipped. +# Empty lines are skipped too. + +# Backup files left behind by the Emacs editor. +*~ + +# Lock files used by the Emacs editor. +# Notice that the "#" character is quoted with a backslash. +# This prevents it from being interpreted as starting a comment. +.\#* + +# Temporary files used by the vim editor. +.*.swp + +# A hidden file created by the Mac OS X Finder. +.DS_Store + + + + + Case sensitivity + + If you're working in a mixed development environment that + contains both Linux (or other Unix) systems and Macs or Windows + systems, you should keep in the back of your mind the knowledge + that they treat the case (N versus + n) of file names in incompatible ways. This is + not very likely to affect you, and it's easy to deal with if it + does, but it could surprise you if you don't know about + it. + + Operating systems and filesystems differ in the way they + handle the case of characters in file and + directory names. There are three common ways to handle case in + names. + + Completely case insensitive. Uppercase and + lowercase versions of a letter are treated as identical, + both when creating a file and during subsequent accesses. + This is common on older DOS-based systems. + + Case preserving, but insensitive. When a file + or directory is created, the case of its name is stored, and + can be retrieved and displayed by the operating system. + When an existing file is being looked up, its case is + ignored. This is the standard arrangement on Windows and + MacOS. The names foo and + FoO identify the same file. This + treatment of uppercase and lowercase letters as + interchangeable is also referred to as case + folding. + + Case sensitive. The case of a name is + significant at all times. The names foo + and {FoO} identify different files. This is the way Linux + and Unix systems normally work. + + + On Unix-like systems, it is possible to have any or all of + the above ways of handling case in action at once. For example, + if you use a USB thumb drive formatted with a FAT32 filesystem + on a Linux system, Linux will handle names on that filesystem in + a case preserving, but insensitive, way. + + + Safe, portable repository storage + + Mercurial's repository storage mechanism is case + safe. It translates file names so that they can + be safely stored on both case sensitive and case insensitive + filesystems. This means that you can use normal file copying + tools to transfer a Mercurial repository onto, for example, a + USB thumb drive, and safely move that drive and repository + back and forth between a Mac, a PC running Windows, and a + Linux box. + + + + Detecting case conflicts + + When operating in the working directory, Mercurial honours + the naming policy of the filesystem where the working + directory is located. If the filesystem is case preserving, + but insensitive, Mercurial will treat names that differ only + in case as the same. + + An important aspect of this approach is that it is + possible to commit a changeset on a case sensitive (typically + Linux or Unix) filesystem that will cause trouble for users on + case insensitive (usually Windows and MacOS) users. If a + Linux user commits changes to two files, one named + myfile.c and the other named + MyFile.C, they will be stored correctly + in the repository. And in the working directories of other + Linux users, they will be correctly represented as separate + files. + + If a Windows or Mac user pulls this change, they will not + initially have a problem, because Mercurial's repository + storage mechanism is case safe. However, once they try to + hg update the working + directory to that changeset, or hg + merge with that changeset, Mercurial will spot the + conflict between the two file names that the filesystem would + treat as the same, and forbid the update or merge from + occurring. + + + + Fixing a case conflict + + If you are using Windows or a Mac in a mixed environment + where some of your collaborators are using Linux or Unix, and + Mercurial reports a case folding conflict when you try to + hg update or hg merge, the procedure to fix the + problem is simple. + + Just find a nearby Linux or Unix box, clone the problem + repository onto it, and use Mercurial's hg rename command to change the + names of any offending files or directories so that they will + no longer cause case folding conflicts. Commit this change, + hg pull or hg push it across to your Windows or + MacOS system, and hg update + to the revision with the non-conflicting names. + + The changeset with case-conflicting names will remain in + your project's history, and you still won't be able to + hg update your working + directory to that changeset on a Windows or MacOS system, but + you can continue development unimpeded. + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch07-branch.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch07-branch.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,532 @@ + + + + + Managing releases and branchy development + + Mercurial provides several mechanisms for you to manage a + project that is making progress on multiple fronts at once. To + understand these mechanisms, let's first take a brief look at a + fairly normal software project structure. + + Many software projects issue periodic major + releases that contain substantial new features. In parallel, they + may issue minor releases. These are usually + identical to the major releases off which they're based, but with + a few bugs fixed. + + In this chapter, we'll start by talking about how to keep + records of project milestones such as releases. We'll then + continue on to talk about the flow of work between different + phases of a project, and how Mercurial can help you to isolate and + manage this work. + + + Giving a persistent name to a revision + + Once you decide that you'd like to call a particular + revision a release, it's a good idea to record + the identity of that revision. This will let you reproduce that + release at a later date, for whatever purpose you might need at + the time (reproducing a bug, porting to a new platform, etc). + &interaction.tag.init; + + Mercurial lets you give a permanent name to any revision + using the hg 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: + + Colon (ASCII 58, + :) + + Carriage return (ASCII 13, + \r) + + Newline (ASCII 10, + \n) + + + You can use the hg tags + command to display the tags present in your repository. In the + output, each tagged revision is identified first by its name, + then by revision number, and finally by the unique hash of the + revision. + + &interaction.tag.tags; + + Notice that tip is listed in the output + of hg tags. The + tip tag is a special floating + tag, which always identifies the newest revision in the + repository. + + In the output of the hg + tags command, tags are listed in reverse order, by + revision number. This usually means that recent tags are listed + before older tags. It also means that tip is + always going to be the first tag listed in the output of + hg tags. + + When you run hg log, if it + displays a revision that has tags associated with it, it will + print those tags. + + &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 + hg 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 hg tag command. You'll have to use the + option to tell Mercurial + that you 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 in the root of your repository named .hgtags. When you run the hg tag command, Mercurial modifies + this file, then automatically commits the change to it. This + means that every time you run hg + tag, you'll see a corresponding changeset in the + output of hg log. + + &interaction.tag.tip; + + + Handling tag conflicts during a merge + + You won't often need to care about the .hgtags file, but it sometimes + makes its presence known during a merge. The format of the + file is simple: it consists of a series of lines. Each line + starts with a changeset hash, followed by a space, followed by + the name of a tag. + + If you're resolving a conflict in the .hgtags file during a merge, + there's one twist to modifying the .hgtags file: when Mercurial is + parsing the tags in a repository, it + never reads the working copy of the + .hgtags file. Instead, it + reads the most recently committed + revision of the file. + + An unfortunate consequence of this design is that you + can't actually verify that your merged .hgtags file is correct until + after you've committed a change. So if + you find yourself resolving a conflict on .hgtags during a merge, be sure to + run hg tags after you commit. + If it finds an error in the .hgtags file, it will report the + location of the error, which you can then fix and commit. You + should then run hg tags + again, just to be sure that your fix is correct. + + + + Tags and cloning + + You may have noticed that the hg + clone command has a option that lets you clone + an exact copy of the repository as of a particular changeset. + The new clone will not contain any project history that comes + after the revision you specified. This has an interaction + with tags that can surprise the unwary. + + Recall that a tag is stored as a revision to the .hgtags file, so that when you + create a tag, the changeset in which it's recorded necessarily + refers to an older changeset. When you run hg clone -r foo to clone a + repository as of tag foo, the new clone + will not contain the history that created the + tag that you used to clone the repository. The + result is that you'll get exactly the right subset of the + project's history in the new repository, but + not the tag you might have + expected. + + + + When permanent tags are too much + + Since Mercurial's tags are revision controlled and carried + around with a project's history, everyone you work with will + see the tags you create. But giving names to revisions has + uses beyond simply noting that revision + 4237e45506ee is really + v2.0.2. If you're trying to track down a + subtle bug, you might want a tag to remind you of something + like Anne saw the symptoms with this + revision. + + For cases like this, what you might want to use are + local tags. You can create a local tag + with the option to the + hg tag command. This will + store the tag in a file called .hg/localtags. Unlike .hgtags, .hg/localtags is not revision + controlled. Any tags you create using remain strictly local to the + repository you're currently working in. + + + + + The flow of changes&emdash;big picture vs. little + + To return to the outline I sketched at the beginning of a + chapter, let's think about a project that has multiple + concurrent pieces of work under development at once. + + There might be a push for a new main release; + a new minor bugfix release to the last main release; and an + unexpected hot fix to an old release that is now + in maintenance mode. + + The usual way people refer to these different concurrent + directions of development is as branches. + However, we've already seen numerous times that Mercurial treats + all of history as a series of branches and + merges. Really, what we have here is two ideas that are + peripherally related, but which happen to share a name. + + Big picture branches represent + the sweep of a project's evolution; people give them names, + and talk about them in conversation. + + Little picture branches are + artefacts of the day-to-day activity of developing and + merging changes. They expose the narrative of how the code + was developed. + + + + + Managing big-picture branches in repositories + + The easiest way to isolate a big picture + branch in Mercurial is in a dedicated repository. If you have + an existing shared repository&emdash;let's call it + myproject&emdash;that reaches a + 1.0 milestone, you can start to prepare for + future maintenance releases on top of version 1.0 by tagging the + revision from which you prepared the 1.0 release. + + &interaction.branch-repo.tag; + + You can then clone a new shared + 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 + 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 myproject repository. + + &interaction.branch-repo.new; + + + + 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; + + + + Naming branches within one repository + + In most instances, isolating branches in repositories is the + right approach. Its simplicity makes it easy to understand; and + so it's hard to make mistakes. There's a one-to-one + relationship between branches you're working in and directories + on your system. This lets you use normal (non-Mercurial-aware) + tools to work on files within a branch/repository. + + If you're more in the power user category + (and your collaborators are too), there is + an alternative way of handling branches that you can consider. + I've already mentioned the human-level distinction between + small picture and big picture + branches. While Mercurial works with multiple small + picture branches in a repository all the time (for + example after you pull changes in, but before you merge them), + it can also work with multiple big + picture branches. + + The key to working this way is that Mercurial lets you + assign a persistent name to a branch. + There always exists a branch named default. + Even before you start naming branches yourself, you can find + traces of the default branch if you look for + them. + + As an example, when you run the hg + commit command, and it pops up your editor so that + you can enter a commit message, look for a line that contains + the text HG: branch default at + the bottom. This is telling you that your commit will occur on + the branch named default. + + To start working with named branches, use the hg branches command. This command + lists the named branches already present in your repository, + telling you which changeset is the tip of each. + + &interaction.branch-named.branches; + + Since you haven't created any named branches yet, the only + one that exists is default. + + To find out what the current branch is, run + the hg branch command, giving + it no arguments. This tells you what branch the parent of the + current changeset is on. + + &interaction.branch-named.branch; + + To create a new branch, run the hg + branch command again. This time, give it one + argument: the name of the branch you want to create. + + &interaction.branch-named.create; + + After you've created a branch, you might wonder what effect + the hg branch command has had. + What do the hg status and + hg tip commands report? + + &interaction.branch-named.status; + + Nothing has changed in the + working directory, and there's been no new history created. As + this suggests, running the hg + branch command has no permanent effect; it only + tells Mercurial what branch name to use the + next time you commit a changeset. + + When you commit a change, Mercurial records the name of the + branch on which you committed. Once you've switched from the + default branch to another and committed, + you'll see the name of the new branch show up in the output of + hg log, hg tip, and other commands that + display the same kind of output. + + &interaction.branch-named.commit; + + The hg log-like commands + will print the branch name of every changeset that's not on the + default branch. As a result, if you never + use named branches, you'll never see this information. + + Once you've named a branch and committed a change with that + name, every subsequent commit that descends from that change + will inherit the same branch name. You can change the name of a + branch at any time, using the hg + branch command. + + &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.) + + + + Dealing with multiple named branches in a + repository + + If you have more than one named branch in a repository, + Mercurial will remember the branch that your working directory + on when you start a command like hg + update or hg pull + -u. It will update the working directory to the tip + of this branch, no matter what the repo-wide tip + is. To update to a revision that's on a different named branch, + you may need to use the + option to hg update. + + This behavior 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 bar branch, but there also + exists an older hg foo + branch. + + We can hg update back and + forth between the tips of the foo and + bar branches without needing to use the + option, because this + only involves going backwards and forwards linearly through our + change history. + + &interaction.branch-named.update-switchy; + + If we go back to the foo branch and then + run hg update, it will keep us + on foo, not move us to the tip of + bar. + + &interaction.branch-named.update-nothing; + + Committing a new change on the foo branch + introduces a new head. + + &interaction.branch-named.foo-commit; + + + + Branch names and merging + + As you've probably noticed, merges in Mercurial are not + symmetrical. Let's say our repository has two heads, 17 and 23. + If I hg update to 17 and then + hg merge with 23, Mercurial + records 17 as the first parent of the merge, and 23 as the + second. Whereas if I hg update + to 23 and then hg merge with + 17, it records 23 as the first parent, and 17 as the + second. + + This affects Mercurial's choice of branch name when you + merge. After a merge, Mercurial will retain the branch name of + the first parent when you commit the result of the merge. If + your first parent's branch name is foo, and + you merge with bar, the branch name will + still be foo after you merge. + + It's not unusual for a repository to contain multiple heads, + each with the same branch name. Let's say I'm working on the + foo branch, and so are you. We commit + different changes; I pull your changes; I now have two heads, + each claiming to be on the foo branch. The + result of a merge will be a single head on the + foo branch, as you might hope. + + But if I'm working on the bar branch, and + I merge work from the foo branch, the result + will remain on the bar branch. + + &interaction.branch-named.merge; + + To give a more concrete example, if I'm working on the + bleeding-edge branch, and I want to bring in + the latest fixes from the stable branch, + Mercurial will choose the right + (bleeding-edge) branch name when I pull and + merge from stable. + + + + Branch naming is generally useful + + You shouldn't think of named branches as applicable only to + situations where you have multiple long-lived branches + cohabiting in a single repository. They're very useful even in + the one-branch-per-repository case. + + In the simplest case, giving a name to each branch gives you + a permanent record of which branch a changeset originated on. + This gives you more context when you're trying to follow the + history of a long-lived branchy project. + + If you're working with shared repositories, you can set up a + pretxnchangegroup hook on each + that will block incoming changes that have the + wrong branch name. This provides a simple, but + effective, defence against people accidentally pushing changes + from a bleeding edge branch to a + stable branch. Such a hook might look like this + inside the shared repo's + /.hgrc. + [hooks] +pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch08-undo.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch08-undo.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1069 @@ + + + + + Finding and fixing mistakes + + To err might be human, but to really handle the consequences + well takes a top-notch revision control system. In this chapter, + we'll discuss some of the techniques you can use when you find + that a problem has crept into your project. Mercurial has some + highly capable features that will help you to isolate the sources + of problems, and to handle them appropriately. + + + Erasing local history + + + The accidental commit + + I have the occasional but persistent problem of typing + rather more quickly than I can think, which sometimes results + in me committing a changeset that is either incomplete or + plain wrong. In my case, the usual kind of incomplete + changeset is one in which I've created a new source file, but + forgotten to hg add it. A + plain wrong changeset is not as common, but no + less annoying. + + + + Rolling back a transaction + + In , I + mentioned that Mercurial treats each modification of a + repository as a transaction. Every time + you commit a changeset or pull changes from another + repository, Mercurial remembers what you did. You can undo, + or roll back, exactly one of these + actions using the hg rollback + command. (See + for an important caveat about the use of this command.) + + Here's a mistake that I often find myself making: + committing a change in which I've created a new file, but + forgotten to hg add + it. + + &interaction.rollback.commit; + + Looking at the output of hg + status after the commit immediately confirms the + error. + + &interaction.rollback.status; + + The commit captured the changes to the file + a, but not the new file + b. If I were to push this changeset to a + repository that I shared with a colleague, the chances are + high that something in a would refer to + b, which would not be present in their + repository when they pulled my changes. I would thus become + the object of some indignation. + + However, luck is with me&emdash;I've caught my error + before I pushed the changeset. I use the hg rollback command, and Mercurial + makes that last changeset vanish. + + &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 a is modified. The + commit and rollback have left the working directory exactly as + it was prior to the commit; the changeset has been completely + erased. I can now safely hg + add the file b, and rerun my + commit. + + &interaction.rollback.add; + + + + The erroneous pull + + It's common practice with Mercurial to maintain separate + development branches of a project in different repositories. + Your development team might have one shared repository for + your project's 0.9 release, and another, + containing different changes, for the 1.0 + release. + + Given this, you can imagine that the consequences could be + messy if you had a local 0.9 repository, and + accidentally pulled changes from the shared 1.0 + repository into it. At worst, you could be paying + insufficient attention, and push those changes into the shared + 0.9 tree, confusing your entire team (but don't + worry, we'll return to this horror scenario later). However, + it's more likely that you'll notice immediately, because + Mercurial will display the URL it's pulling from, or you will + see it pull a suspiciously large number of changes into the + repository. + + The hg rollback command + will work nicely to expunge all of the changesets that you + just pulled. Mercurial groups all changes from one hg pull into a single transaction, + so one hg rollback is all you + need to undo this mistake. + + + + Rolling back is useless once you've pushed + + The value of the hg + rollback command drops to zero once you've pushed + your changes to another repository. Rolling back a change + makes it disappear entirely, but only in + the repository in which you perform the hg rollback. Because a rollback + eliminates history, there's no way for the disappearance of a + change to propagate between repositories. + + If you've pushed a change to another + repository&emdash;particularly if it's a shared + repository&emdash;it has essentially escaped into the + wild, and you'll have to recover from your mistake + in a different way. What will happen if you push a changeset + somewhere, then roll it back, then pull from the repository + you pushed to, is that the changeset will reappear in your + repository. + + (If you absolutely know for sure that the change you want + to roll back is the most recent change in the repository that + you pushed to, and you know that nobody + else could have pulled it from that repository, you can roll + back the changeset there, too, but you really should really + not rely on this working reliably. If you do this, sooner or + later a change really will make it into a repository that you + don't directly control (or have forgotten about), and come + back to bite you.) + + + + You can only roll back once + + Mercurial stores exactly one transaction in its + transaction log; that transaction is the most recent one that + occurred in the repository. This means that you can only roll + back one transaction. If you expect to be able to roll back + one transaction, then its predecessor, this is not the + behavior 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. + + + + + Reverting the mistaken change + + If you make a modification to a file, and decide that you + really didn't want to change the file at all, and you haven't + yet committed your changes, the hg + revert command is the one you'll need. It looks at + the changeset that's the parent of the working directory, and + restores the contents of the file to their state as of that + changeset. (That's a long-winded way of saying that, in the + normal case, it undoes your modifications.) + + Let's illustrate how the hg + revert command works with yet another small example. + We'll begin by modifying a file that Mercurial is already + tracking. + + &interaction.daily.revert.modify; + + If we don't + want that change, we can simply hg + revert the file. + + &interaction.daily.revert.unmodify; + + The hg revert command + provides us with an extra degree of safety by saving our + modified file with a .orig + extension. + + &interaction.daily.revert.status; + + Here is a summary of the cases that the hg revert command can deal with. We + will describe each of these in more detail in the section that + follows. + + If you modify a file, it will restore the file + to its unmodified state. + + If you hg add a + file, it will undo the added state of the + file, but leave the file itself untouched. + + If you delete a file without telling Mercurial, + it will restore the file to its unmodified contents. + + If you use the hg + remove command to remove a file, it will undo + the removed state of the file, and restore + the file to its unmodified contents. + + + + File management errors + + The hg revert command is + useful for more than just modified files. It lets you reverse + the results of all of Mercurial's file management + commands&emdash;hg add, + hg remove, and so on. + + If you hg add a file, + then decide that in fact you don't want Mercurial to track it, + use hg revert to undo the + add. Don't worry; Mercurial will not modify the file in any + way. It will just unmark the file. + + &interaction.daily.revert.add; + + Similarly, if you ask Mercurial to hg remove a file, you can use + hg revert to restore it to + the contents it had as of the parent of the working directory. + &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 hg copy, + the copied-to file remains in your working directory + afterwards, untracked. Since a copy doesn't affect the + copied-from file in any way, Mercurial doesn't do anything + with the copied-from file. + + &interaction.daily.revert.copy; + + + A slightly special case: reverting a rename + + If you hg rename a + file, there is one small detail that you should remember. + When you hg revert a + rename, it's not enough to provide the name of the + renamed-to file, as you can see here. + + &interaction.daily.revert.rename; + + As you can see from the output of hg status, the renamed-to file is + no longer identified as added, but the + renamed-from file is still removed! + This is counter-intuitive (at least to me), but at least + it's easy to deal with. + + &interaction.daily.revert.rename-orig; + + So remember, to revert a hg + rename, you must provide + both the source and destination + names. + + % TODO: the output doesn't look like it will be + removed! + + (By the way, if you rename a file, then modify the + renamed-to file, then revert both components of the rename, + when Mercurial restores the file that was removed as part of + the rename, it will be unmodified. If you need the + modifications in the renamed-to file to show up in the + renamed-from file, don't forget to copy them over.) + + These fiddly aspects of reverting a rename arguably + constitute a small bug in Mercurial. + + + + + + Dealing with committed changes + + Consider a case where you have committed a change $a$, and + another change $b$ on top of it; you then realise that change + $a$ was incorrect. Mercurial lets you back out + an entire changeset automatically, and building blocks that let + you reverse part of a changeset by hand. + + Before you read this section, here's something to + keep in mind: the hg backout + command undoes changes by adding history, + not by modifying or erasing it. It's the right tool to use if + you're fixing bugs, but not if you're trying to undo some change + that has catastrophic consequences. To deal with those, see + . + + + Backing out a changeset + + The hg backout command + lets you undo the effects of an entire + changeset in an automated fashion. Because Mercurial's + history is immutable, this command does + not get rid of the changeset you want to undo. + Instead, it creates a new changeset that + reverses the effect of the to-be-undone + changeset. + + The operation of the hg + backout command is a little intricate, so let's + illustrate it with some examples. First, we'll create a + repository with some simple changes. + + &interaction.backout.init; + + The hg backout command + takes a single changeset ID as its argument; this is the + changeset to back out. Normally, hg + backout will drop you into a text editor to write + a commit message, so you can record why you're backing the + change out. In this example, we provide a commit message on + the command line using the option. + + + + Backing out the tip changeset + + We're going to start by backing out the last changeset we + committed. + + &interaction.backout.simple; + + You can see that the second line from + myfile is no longer present. Taking a + look at the output of hg log + gives us an idea of what the hg + backout command has done. + &interaction.backout.simple.log; Notice that the new changeset + that hg backout has created + is a child of the changeset we backed out. It's easier to see + this in , which presents a + graphical view of the change history. As you can see, the + history is nice and linear. + +
    + Backing out a change using the <command + role="hg-cmd">hg backout</command> command + + + XXX add text + +
    + +
    + + Backing out a non-tip change + + If you want to back out a change other than the last one + you committed, pass the option to the + hg backout command. + + &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 + 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 illustrates, Mercurial + actually commits two changes in this kind + of situation (the box-shaped nodes are the ones that Mercurial + commits automatically). Before Mercurial begins the backout + process, it first remembers what the current parent of the + working directory is. It then backs out the target changeset, + and commits that as a changeset. Finally, it merges back to + the previous parent of the working directory, and commits the + result of the merge. + + % TODO: to me it looks like mercurial doesn't commit the + second merge automatically! + +
    + Automated backout of a non-tip change using the + <command role="hg-cmd">hg backout</command> command + + + XXX add text + +
    + + The result is that you end up back where you + were, only with some extra history that undoes the + effect of the changeset you wanted to back out. + + + Always use the <option + role="hg-opt-backout">--merge</option> option + + In fact, since the option will do the + right thing whether or not the changeset + you're backing out is the tip (i.e. it won't try to merge if + it's backing out the tip, since there's no need), you should + always use this option when you run the + hg backout command. + + +
    + + Gaining more control of the backout process + + While I've recommended that you always use the option when backing + out a change, the hg backout + command lets you decide how to merge a backout changeset. + Taking control of the backout process by hand is something you + will rarely need to do, but it can be useful to understand + what the hg backout command + is doing for you automatically. To illustrate this, let's + clone our first repository, but omit the backout change that + it contains. + + &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, not + a descendant of the changeset that was the tip. The hg 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 . This makes it clear + that when we use hg backout + to back out a change other than the tip, Mercurial adds a new + head to the repository (the change it committed is + box-shaped). + +
    + Backing out a change using the <command + role="hg-cmd">hg backout</command> command + + + XXX add text + +
    + + After the hg 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 + myfile now. The first change should be + present, because we've never backed it out. The second change + should be missing, as that's the change we backed out. Since + the history graph shows the third change as a separate head, + we don't expect to see the third change + present in myfile. + + &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 + . + +
    + Manually merging a backout change + + + XXX add text + +
    + +
    + + Why <command role="hg-cmd">hg backout</command> works as + it does + + Here's a brief description of how the hg backout command works. + + It ensures that the working directory is + clean, i.e. that the output of hg status would be empty. + + It remembers the current parent of the working + directory. Let's call this changeset + orig + + It does the equivalent of a hg update to sync the working + directory to the changeset you want to back out. Let's + call this changeset backout + + It finds the parent of that changeset. Let's + call that changeset parent. + + For each file that the + backout changeset affected, it does the + equivalent of a hg revert -r + parent on that file, to restore it to the + contents it had before that changeset was + committed. + + It commits the result as a new changeset. + This changeset has backout as its + parent. + + If you specify on the command + line, it merges with orig, and commits + the result of the merge. + + + An alternative way to implement the hg backout command would be to + hg export the + to-be-backed-out changeset as a diff, then use the option to the + patch command to reverse the effect of the + change without fiddling with the working directory. This + sounds much simpler, but it would not work nearly as + well. + + The reason that hg + backout does an update, a commit, a merge, and + another commit is to give the merge machinery the best chance + to do a good job when dealing with all the changes + between the change you're backing out and + the current tip. + + If you're backing out a changeset that's 100 revisions + back in your project's history, the chances that the + patch command will be able to apply a + reverse diff cleanly are not good, because intervening changes + are likely to have broken the context that + patch uses to determine whether it can + apply a patch (if this sounds like gibberish, see for a + discussion of the patch command). Also, + Mercurial's merge machinery will handle files and directories + being renamed, permission changes, and modifications to binary + files, none of which patch can deal + with. + + +
    + + Changes that should never have been + + Most of the time, the hg + backout command is exactly what you need if you want + to undo the effects of a change. It leaves a permanent record + of exactly what you did, both when committing the original + changeset and when you cleaned up after it. + + On rare occasions, though, you may find that you've + committed a change that really should not be present in the + repository at all. For example, it would be very unusual, and + usually considered a mistake, to commit a software project's + object files as well as its source files. Object files have + almost no intrinsic value, and they're big, + so they increase the size of the repository and the amount of + time it takes to clone or pull changes. + + Before I discuss the options that you have if you commit a + brown paper bag change (the kind that's so bad + that you want to pull a brown paper bag over your head), let me + first discuss some approaches that probably won't work. + + Since Mercurial treats history as + accumulative&emdash;every change builds on top of all changes + that preceded it&emdash;you generally can't just make disastrous + changes disappear. The one exception is when you've just + committed a change, and it hasn't been pushed or pulled into + another repository. That's when you can safely use the hg rollback command, as I detailed in + . + + After you've pushed a bad change to another repository, you + could still use hg + rollback to make your local copy of the change + disappear, but it won't have the consequences you want. The + change will still be present in the remote repository, so it + will reappear in your local repository the next time you + pull. + + If a situation like this arises, and you know which + repositories your bad change has propagated into, you can + try to get rid of the changeefrom + every one of those repositories. This is, + of course, not a satisfactory solution: if you miss even a + single repository while you're expunging, the change is still + in the wild, and could propagate further. + + If you've committed one or more changes + after the change that you'd like to see + disappear, your options are further reduced. Mercurial doesn't + provide a way to punch a hole in history, leaving + changesets intact. + + XXX This needs filling out. The + hg-replay script in the + examples directory works, but doesn't handle + merge changesets. Kind of an important omission. + + + Protect yourself from <quote>escaped</quote> + changes + + If you've committed some changes to your local repository + and they've been pushed or pulled somewhere else, this isn't + necessarily a disaster. You can protect yourself ahead of + time against some classes of bad changeset. This is + particularly easy if your team usually pulls changes from a + central repository. + + By configuring some hooks on that repository to validate + incoming changesets (see chapter ), + you can + automatically prevent some kinds of bad changeset from being + pushed to the central repository at all. With such a + configuration in place, some kinds of bad changeset will + naturally tend to die out because they can't + propagate into the central repository. Better yet, this + happens without any need for explicit intervention. + + For instance, an incoming change hook that verifies that a + changeset will actually compile can prevent people from + inadvertantly breaking the build. + + + + + Finding the source of a bug + + While it's all very well to be able to back out a changeset + that introduced a bug, this requires that you know which + changeset to back out. Mercurial provides an invaluable + command, called hg bisect, that + helps you to automate this process and accomplish it very + efficiently. + + The idea behind the hg + bisect command is that a changeset has introduced + some change of behavior that you can identify with a simple + binary test. You don't know which piece of code introduced the + change, but you know how to test for the presence of the bug. + The hg bisect command uses your + test to direct its search for the changeset that introduced the + code that caused the bug. + + Here are a few scenarios to help you understand how you + might apply this command. + + The most recent version of your software has a + bug that you remember wasn't present a few weeks ago, but + you don't know when it was introduced. Here, your binary + test checks for the presence of that bug. + + You fixed a bug in a rush, and now it's time to + close the entry in your team's bug database. The bug + database requires a changeset ID when you close an entry, + but you don't remember which changeset you fixed the bug in. + Once again, your binary test checks for the presence of the + bug. + + Your software works correctly, but runs 15% + slower than the last time you measured it. You want to know + which changeset introduced the performance regression. In + this case, your binary test measures the performance of your + software, to see whether it's fast or + slow. + + The sizes of the components of your project that + you ship exploded recently, and you suspect that something + changed in the way you build your project. + + + From these examples, it should be clear that the hg bisect command is not useful only + for finding the sources of bugs. You can use it to find any + emergent property of a repository (anything that + you can't find from a simple text search of the files in the + tree) for which you can write a binary test. + + We'll introduce a little bit of terminology here, just to + make it clear which parts of the search process are your + responsibility, and which are Mercurial's. A + test is something that + you run when hg + bisect chooses a changeset. A + probe is what hg + bisect runs to tell whether a revision is good. + Finally, we'll use the word bisect, as both a + noun and a verb, to stand in for the phrase search using + the hg bisect + command. + + One simple way to automate the searching process would be + simply to probe every changeset. However, this scales poorly. + If it took ten minutes to test a single changeset, and you had + 10,000 changesets in your repository, the exhaustive approach + would take on average 35 days to find the + changeset that introduced a bug. Even if you knew that the bug + was introduced by one of the last 500 changesets, and limited + your search to those, you'd still be looking at over 40 hours to + find the changeset that introduced your bug. + + What the hg bisect command + does is use its knowledge of the shape of your + project's revision history to perform a search in time + proportional to the logarithm of the number + of changesets to check (the kind of search it performs is called + a dichotomic search). With this approach, searching through + 10,000 changesets will take less than three hours, even at ten + minutes per test (the search will require about 14 tests). + Limit your search to the last hundred changesets, and it will + take only about an hour (roughly seven tests). + + The hg bisect command is + aware of the branchy nature of a Mercurial + project's revision history, so it has no problems dealing with + branches, merges, or multiple heads in a repository. It can + prune entire branches of history with a single probe, which is + how it operates so efficiently. + + + Using the <command role="hg-cmd">hg bisect</command> + command + + Here's an example of hg + bisect in action. + + + In versions 0.9.5 and earlier of Mercurial, hg bisect was not a core command: + it was distributed with Mercurial as an extension. This + section describes the built-in command, not the old + extension. + + + Now let's create a repository, so that we can try out the + hg bisect command in + isolation. + + &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 hg bisect command. + We can use Mercurial's normal built-in help mechanism for + this. + + &interaction.bisect.help; + + The hg bisect command + works in steps. Each step proceeds as follows. + + You run your binary test. + + If the test succeeded, you tell hg bisect by running the + hg bisect good + command. + + If it failed, run the hg bisect bad + command. + + The command uses your information to decide + which changeset to test next. + + It updates the working directory to that + changeset, and the process begins again. + + The process ends when hg + bisect identifies a unique changeset that marks + the point where your test transitioned from + succeeding to failing. + + To start the search, we must run the hg bisect --reset command. + + &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 + doesn't have the bug; the hg bisect command will + bracket its search between the first pair of + good and bad changesets. In our case, we know that revision + 10 didn't have the bug. (I'll have more words about choosing + the first good changeset later.) + + &interaction.bisect.search.good-init; + + Notice that this command printed some output. + + It told us how many changesets it must + consider before it can identify the one that introduced + the bug, and how many tests that will require. + + It updated the working directory to the next + changeset to test, and told us which changeset it's + testing. + + + We now run our test in the working directory. We use the + grep command to see if our + bad file is present in the working directory. + If it is, this revision is bad; if not, this revision is good. + &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, + 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 + hg bisect command let us find + the changeset that introduced our bug with only + five tests. Because the number of tests that the hg bisect command performs grows + logarithmically with the number of changesets to search, the + advantage that it has over the brute force + search approach increases with every changeset you add. + + + + Cleaning up after your search + + When you're finished using the hg + bisect command in a repository, you can use the + hg bisect reset command to + drop the information it was using to drive your search. The + command doesn't use much space, so it doesn't matter if you + forget to run this command. However, hg bisect won't let you start a new + search in that repository until you do a hg bisect reset. + + &interaction.bisect.search.reset; + + + + + Tips for finding bugs effectively + + + Give consistent input + + The hg bisect command + requires that you correctly report the result of every test + you perform. If you tell it that a test failed when it really + succeeded, it might be able to detect the + inconsistency. If it can identify an inconsistency in your + reports, it will tell you that a particular changeset is both + good and bad. However, it can't do this perfectly; it's about + as likely to report the wrong changeset as the source of the + bug. + + + + Automate as much as possible + + When I started using the hg + bisect command, I tried a few times to run my + tests by hand, on the command line. This is an approach that + I, at least, am not suited to. After a few tries, I found + that I was making enough mistakes that I was having to restart + my searches several times before finally getting correct + results. + + My initial problems with driving the hg bisect command by hand occurred + even with simple searches on small repositories; if the + problem you're looking for is more subtle, or the number of + tests that hg bisect must + perform increases, the likelihood of operator error ruining + the search is much higher. Once I started automating my + tests, I had much better results. + + The key to automated testing is twofold: + + always test for the same symptom, and + + always feed consistent input to the hg bisect command. + + In my tutorial example above, the grep + command tests for the symptom, and the if + statement takes the result of this check and ensures that we + always feed the same input to the hg + bisect command. The mytest + function marries these together in a reproducible way, so that + every test is uniform and consistent. + + + + Check your results + + Because the output of a hg + bisect search is only as good as the input you + give it, don't take the changeset it reports as the absolute + truth. A simple way to cross-check its report is to manually + run your test at each of the following changesets: + + The changeset that it reports as the first bad + revision. Your test should still report this as + bad. + + The parent of that changeset (either parent, + if it's a merge). Your test should report this changeset + as good. + + A child of that changeset. Your test should + report this changeset as bad. + + + + + Beware interference between bugs + + It's possible that your search for one bug could be + disrupted by the presence of another. For example, let's say + your software crashes at revision 100, and worked correctly at + revision 50. Unknown to you, someone else introduced a + different crashing bug at revision 60, and fixed it at + revision 80. This could distort your results in one of + several ways. + + It is possible that this other bug completely + masks yours, which is to say that it occurs + before your bug has a chance to manifest itself. If you can't + avoid that other bug (for example, it prevents your project + from building), and so can't tell whether your bug is present + in a particular changeset, the hg + bisect command cannot help you directly. Instead, + you can mark a changeset as untested by running hg bisect --skip. + + A different problem could arise if your test for a bug's + presence is not specific enough. If you check for my + program crashes, then both your crashing bug and an + unrelated crashing bug that masks it will look like the same + thing, and mislead hg + bisect. + + Another useful situation in which to use hg bisect --skip is if you can't + test a revision because your project was in a broken and hence + untestable state at that revision, perhaps because someone + checked in a change that prevented the project from + building. + + + + Bracket your search lazily + + Choosing the first good and + bad changesets that will mark the end points of + your search is often easy, but it bears a little discussion + nevertheless. From the perspective of hg bisect, the newest + changeset is conventionally bad, and the older + changeset is good. + + If you're having trouble remembering when a suitable + good change was, so that you can tell hg bisect, you could do worse than + testing changesets at random. Just remember to eliminate + contenders that can't possibly exhibit the bug (perhaps + because the feature with the bug isn't present yet) and those + where another problem masks the bug (as I discussed + above). + + Even if you end up early by thousands of + changesets or months of history, you will only add a handful + of tests to the total number that hg + bisect must perform, thanks to its logarithmic + behavior. + + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch09-hook.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch09-hook.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,2038 @@ + + + + + Handling repository events with hooks + + Mercurial offers a powerful mechanism to let you perform + automated actions in response to events that occur in a + repository. In some cases, you can even control Mercurial's + response to those events. + + The name Mercurial uses for one of these actions is a + hook. Hooks are called + triggers in some revision control systems, but the + two names refer to the same idea. + + + An overview of hooks in Mercurial + + Here is a brief list of the hooks that Mercurial + supports. We will revisit each of these hooks in more detail + later, in . + + + changegroup: This + is run after a group of changesets has been brought into the + repository from elsewhere. + + commit: This is + run after a new changeset has been created in the local + repository. + + incoming: This is + run once for each new changeset that is brought into the + repository from elsewhere. Notice the difference from + changegroup, which is run + once per group of changesets brought + in. + + outgoing: This is + run after a group of changesets has been transmitted from + this repository. + + prechangegroup: + This is run before starting to bring a group of changesets + into the repository. + + + precommit: + Controlling. This is run before starting a commit. + + + preoutgoing: + Controlling. This is run before starting to transmit a group + of changesets from this repository. + + + pretag: + Controlling. This is run before creating a tag. + + + pretxnchangegroup: Controlling. This + is run after a group of changesets has been brought into the + local repository from another, but before the transaction + completes that will make the changes permanent in the + repository. + + + pretxncommit: + Controlling. This is run after a new changeset has been + created in the local repository, but before the transaction + completes that will make it permanent. + + + preupdate: + Controlling. This is run before starting an update or merge + of the working directory. + + + tag: This is run + after a tag is created. + + + update: This is + run after an update or merge of the working directory has + finished. + + + Each of the hooks whose description begins with the word + Controlling has the ability to determine whether + an activity can proceed. If the hook succeeds, the activity may + proceed; if it fails, the activity is either not permitted or + undone, depending on the hook. + + + + + Hooks and security + + + Hooks are run with your privileges + + When you run a Mercurial command in a repository, and the + command causes a hook to run, that hook runs on + your system, under + your user account, with + your privilege level. Since hooks are + arbitrary pieces of executable code, you should treat them + with an appropriate level of suspicion. Do not install a hook + unless you are confident that you know who created it and what + it does. + + + In some cases, you may be exposed to hooks that you did + not install yourself. If you work with Mercurial on an + unfamiliar system, Mercurial will run hooks defined in that + system's global ~/.hgrc + file. + + + If you are working with a repository owned by another + user, Mercurial can run hooks defined in that user's + repository, but it will still run them as you. + For example, if you hg pull + from that repository, and its .hg/hgrc defines a local outgoing hook, that hook will run + under your user account, even though you don't own that + repository. + + + + This only applies if you are pulling from a repository + on a local or network filesystem. If you're pulling over + http or ssh, any outgoing + hook will run under whatever account is executing the server + process, on the server. + + + + XXX To see what hooks are defined in a repository, use the + hg config hooks command. If + you are working in one repository, but talking to another that + you do not own (e.g. using hg + pull or hg + incoming), remember that it is the other + repository's hooks you should be checking, not your own. + + + + + Hooks do not propagate + + In Mercurial, hooks are not revision controlled, and do + not propagate when you clone, or pull from, a repository. The + reason for this is simple: a hook is a completely arbitrary + piece of executable code. It runs under your user identity, + with your privilege level, on your machine. + + + It would be extremely reckless for any distributed + revision control system to implement revision-controlled + hooks, as this would offer an easily exploitable way to + subvert the accounts of users of the revision control system. + + + Since Mercurial does not propagate hooks, if you are + collaborating with other people on a common project, you + should not assume that they are using the same Mercurial hooks + as you are, or that theirs are correctly configured. You + should document the hooks you expect people to use. + + + In a corporate intranet, this is somewhat easier to + control, as you can for example provide a + standard installation of Mercurial on an NFS + filesystem, and use a site-wide ~/.hgrc file to define hooks that all users will + see. However, this too has its limits; see below. + + + + + Hooks can be overridden + + Mercurial allows you to override a hook definition by + redefining the hook. You can disable it by setting its value + to the empty string, or change its behavior as you wish. + + + If you deploy a system- or site-wide ~/.hgrc file that defines some + hooks, you should thus understand that your users can disable + or override those hooks. + + + + + Ensuring that critical hooks are run + + Sometimes you may want to enforce a policy that you do not + want others to be able to work around. For example, you may + have a requirement that every changeset must pass a rigorous + set of tests. Defining this requirement via a hook in a + site-wide ~/.hgrc won't + work for remote users on laptops, and of course local users + can subvert it at will by overriding the hook. + + + Instead, you can set up your policies for use of Mercurial + so that people are expected to propagate changes through a + well-known canonical server that you have + locked down and configured appropriately. + + + One way to do this is via a combination of social + engineering and technology. Set up a restricted-access + account; users can push changes over the network to + repositories managed by this account, but they cannot log into + the account and run normal shell commands. In this scenario, + a user can commit a changeset that contains any old garbage + they want. + + + When someone pushes a changeset to the server that + everyone pulls from, the server will test the changeset before + it accepts it as permanent, and reject it if it fails to pass + the test suite. If people only pull changes from this + filtering server, it will serve to ensure that all changes + that people pull have been automatically vetted. + + + + + + Care with <literal>pretxn</literal> hooks in a + shared-access repository + + If you want to use hooks to do some automated work in a + repository that a number of people have shared access to, you + need to be careful in how you do this. + + + Mercurial only locks a repository when it is writing to the + repository, and only the parts of Mercurial that write to the + repository pay attention to locks. Write locks are necessary to + prevent multiple simultaneous writers from scribbling on each + other's work, corrupting the repository. + + + Because Mercurial is careful with the order in which it + reads and writes data, it does not need to acquire a lock when + it wants to read data from the repository. The parts of + Mercurial that read from the repository never pay attention to + locks. This lockless reading scheme greatly increases + performance and concurrency. + + + With great performance comes a trade-off, though, one which + has the potential to cause you trouble unless you're aware of + it. To describe this requires a little detail about how + Mercurial adds changesets to a repository and reads those + changes. + + + When Mercurial writes metadata, it + writes it straight into the destination file. It writes file + data first, then manifest data (which contains pointers to the + new file data), then changelog data (which contains pointers to + the new manifest data). Before the first write to each file, it + stores a record of where the end of the file was in its + transaction log. If the transaction must be rolled back, + Mercurial simply truncates each file back to the size it was + before the transaction began. + + + When Mercurial reads metadata, it reads + the changelog first, then everything else. Since a reader will + only access parts of the manifest or file metadata that it can + see in the changelog, it can never see partially written data. + + + Some controlling hooks (pretxncommit and pretxnchangegroup) run when a + transaction is almost complete. All of the metadata has been + written, but Mercurial can still roll the transaction back and + cause the newly-written data to disappear. + + + If one of these hooks runs for long, it opens a window of + time during which a reader can see the metadata for changesets + that are not yet permanent, and should not be thought of as + really there. The longer the hook runs, the + longer that window is open. + + + + The problem illustrated + + In principle, a good use for the pretxnchangegroup hook would be to + automatically build and test incoming changes before they are + accepted into a central repository. This could let you + guarantee that nobody can push changes to this repository that + break the build. But if a client can pull + changes while they're being tested, the usefulness of the test + is zero; an unsuspecting someone can pull untested changes, + potentially breaking their build. + + + The safest technological answer to this challenge is to + set up such a gatekeeper repository as + unidirectional. Let it take changes + pushed in from the outside, but do not allow anyone to pull + changes from it (use the preoutgoing hook to lock it down). + Configure a changegroup hook so + that if a build or test succeeds, the hook will push the new + changes out to another repository that people + can pull from. + + + In practice, putting a centralised bottleneck like this in + place is not often a good idea, and transaction visibility has + nothing to do with the problem. As the size of a + project&emdash;and the time it takes to build and + test&emdash;grows, you rapidly run into a wall with this + try before you buy approach, where you have + more changesets to test than time in which to deal with them. + The inevitable result is frustration on the part of all + involved. + + + An approach that scales better is to get people to build + and test before they push, then run automated builds and tests + centrally after a push, to be sure all is + well. The advantage of this approach is that it does not + impose a limit on the rate at which the repository can accept + changes. + + + + + + A short tutorial on using hooks + + It is easy to write a Mercurial hook. Let's start with a + hook that runs when you finish a hg + commit, and simply prints the hash of the changeset + you just created. The hook is called commit. + + + All hooks follow the pattern in this example. + +&interaction.hook.simple.init; + + You add an entry to the hooks section of your ~/.hgrc. On the left is the name of + the event to trigger on; on the right is the action to take. As + you can see, you can run an arbitrary shell command in a hook. + Mercurial passes extra information to the hook using environment + variables (look for HG_NODE in the example). + + + + Performing multiple actions per event + + Quite often, you will want to define more than one hook + for a particular kind of event, as shown below. + +&interaction.hook.simple.ext; + + Mercurial lets you do this by adding an + extension to the end of a hook's name. + You extend a hook's name by giving the name of the hook, + followed by a full stop (the + . character), followed by + some more text of your choosing. For example, Mercurial will + run both commit.foo and + commit.bar when the + commit event occurs. + + + To give a well-defined order of execution when there are + multiple hooks defined for an event, Mercurial sorts hooks by + extension, and executes the hook commands in this sorted + order. In the above example, it will execute + commit.bar before + commit.foo, and commit + before both. + + + It is a good idea to use a somewhat descriptive + extension when you define a new hook. This will help you to + remember what the hook was for. If the hook fails, you'll get + an error message that contains the hook name and extension, so + using a descriptive extension could give you an immediate hint + as to why the hook failed (see for an example). + + + + + Controlling whether an activity can proceed + + In our earlier examples, we used the commit hook, which is run after a + commit has completed. This is one of several Mercurial hooks + that run after an activity finishes. Such hooks have no way + of influencing the activity itself. + + + Mercurial defines a number of events that occur before an + activity starts; or after it starts, but before it finishes. + Hooks that trigger on these events have the added ability to + choose whether the activity can continue, or will abort. + + + The pretxncommit hook runs + after a commit has all but completed. In other words, the + metadata representing the changeset has been written out to + disk, but the transaction has not yet been allowed to + complete. The pretxncommit + hook has the ability to decide whether the transaction can + complete, or must be rolled back. + + + If the pretxncommit hook + exits with a status code of zero, the transaction is allowed + to complete; the commit finishes; and the commit hook is run. If the pretxncommit hook exits with a + non-zero status code, the transaction is rolled back; the + metadata representing the changeset is erased; and the + commit hook is not run. + + +&interaction.hook.simple.pretxncommit; + + The hook in the example above checks that a commit comment + contains a bug ID. If it does, the commit can complete. If + not, the commit is rolled back. + + + + + + Writing your own hooks + + When you are writing a hook, you might find it useful to run + Mercurial either with the option, or the verbose config item set to + true. When you do so, Mercurial will print a + message before it calls each hook. + + + + Choosing how your hook should run + + You can write a hook either as a normal + program&emdash;typically a shell script&emdash;or as a Python + function that is executed within the Mercurial process. + + + Writing a hook as an external program has the advantage + that it requires no knowledge of Mercurial's internals. You + can call normal Mercurial commands to get any added + information you need. The trade-off is that external hooks + are slower than in-process hooks. + + + An in-process Python hook has complete access to the + Mercurial API, and does not shell out to + another process, so it is inherently faster than an external + hook. It is also easier to obtain much of the information + that a hook requires by using the Mercurial API than by + running Mercurial commands. + + + If you are comfortable with Python, or require high + performance, writing your hooks in Python may be a good + choice. However, when you have a straightforward hook to + write and you don't need to care about performance (probably + the majority of hooks), a shell script is perfectly fine. + + + + + Hook parameters + + Mercurial calls each hook with a set of well-defined + parameters. In Python, a parameter is passed as a keyword + argument to your hook function. For an external program, a + parameter is passed as an environment variable. + + + Whether your hook is written in Python or as a shell + script, the hook-specific parameter names and values will be + the same. A boolean parameter will be represented as a + boolean value in Python, but as the number 1 (for + true) or 0 (for false) as an + environment variable for an external hook. If a hook + parameter is named foo, the keyword + argument for a Python hook will also be named + foo, while the environment variable for an + external hook will be named HG_FOO. + + + + + Hook return values and activity control + + A hook that executes successfully must exit with a status + of zero if external, or return boolean false if + in-process. Failure is indicated with a non-zero exit status + from an external hook, or an in-process hook returning boolean + true. If an in-process hook raises an + exception, the hook is considered to have failed. + + + For a hook that controls whether an activity can proceed, + zero/false means allow, while + non-zero/true/exception means deny. + + + + + Writing an external hook + + When you define an external hook in your ~/.hgrc and the hook is run, its + value is passed to your shell, which interprets it. This + means that you can use normal shell constructs in the body of + the hook. + + + An executable hook is always run with its current + directory set to a repository's root directory. + + + Each hook parameter is passed in as an environment + variable; the name is upper-cased, and prefixed with the + string HG_. + + + With the exception of hook parameters, Mercurial does not + set or modify any environment variables when running a hook. + This is useful to remember if you are writing a site-wide hook + that may be run by a number of different users with differing + environment variables set. In multi-user situations, you + should not rely on environment variables being set to the + values you have in your environment when testing the hook. + + + + + Telling Mercurial to use an in-process hook + + The ~/.hgrc syntax + for defining an in-process hook is slightly different than for + an executable hook. The value of the hook must start with the + text python:, and continue + with the fully-qualified name of a callable object to use as + the hook's value. + + + The module in which a hook lives is automatically imported + when a hook is run. So long as you have the module name and + PYTHONPATH right, it should just + work. + + + The following ~/.hgrc + example snippet illustrates the syntax and meaning of the + notions we just described. + + [hooks] +commit.example = python:mymodule.submodule.myhook + When Mercurial runs the commit.example + hook, it imports mymodule.submodule, looks + for the callable object named myhook, and + calls it. + + + + + Writing an in-process hook + + The simplest in-process hook does nothing, but illustrates + the basic shape of the hook API: + + def myhook(ui, repo, **kwargs): + pass + The first argument to a Python hook is always a ui object. The second + is a repository object; at the moment, it is always an + instance of localrepository. + Following these two arguments are other keyword arguments. + Which ones are passed in depends on the hook being called, but + a hook can ignore arguments it doesn't care about by dropping + them into a keyword argument dict, as with + **kwargs above. + + + + + + Some hook examples + + + Writing meaningful commit messages + + It's hard to imagine a useful commit message being very + short. The simple pretxncommit + hook of the example below will prevent you from committing a + changeset with a message that is less than ten bytes long. + + +&interaction.hook.msglen.go; + + + + Checking for trailing whitespace + + An interesting use of a commit-related hook is to help you + to write cleaner code. A simple example of cleaner + code is the dictum that a change should not add any + new lines of text that contain trailing + whitespace. Trailing whitespace is a series of + space and tab characters at the end of a line of text. In + most cases, trailing whitespace is unnecessary, invisible + noise, but it is occasionally problematic, and people often + prefer to get rid of it. + + + You can use either the precommit or pretxncommit hook to tell whether you + have a trailing whitespace problem. If you use the precommit hook, the hook will not know + which files you are committing, so it will have to check every + modified file in the repository for trailing white space. If + you want to commit a change to just the file + foo, but the file + bar contains trailing whitespace, doing a + check in the precommit hook + will prevent you from committing foo due + to the problem with bar. This doesn't + seem right. + + + Should you choose the pretxncommit hook, the check won't + occur until just before the transaction for the commit + completes. This will allow you to check for problems only the + exact files that are being committed. However, if you entered + the commit message interactively and the hook fails, the + transaction will roll back; you'll have to re-enter the commit + message after you fix the trailing whitespace and run hg commit again. + + +&interaction.hook.ws.simple; + + In this example, we introduce a simple pretxncommit hook that checks for + trailing whitespace. This hook is short, but not very + helpful. It exits with an error status if a change adds a + line with trailing whitespace to any file, but does not print + any information that might help us to identify the offending + file or line. It also has the nice property of not paying + attention to unmodified lines; only lines that introduce new + trailing whitespace cause problems. + + + The above version 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 the + option to hg commit to reuse + the saved commit message once you've corrected the problem. + + +&interaction.hook.ws.better; + + As a final aside, note in the example above the use of + perl's in-place editing feature to get rid + of trailing whitespace from a file. This is concise and + useful enough that I will reproduce it here. + + perl -pi -e 's,\s+$,,' filename + + + + + Bundled hooks + + Mercurial ships with several bundled hooks. You can find + them in the hgext + directory of a Mercurial source tree. If you are using a + Mercurial binary package, the hooks will be located in the + hgext directory of + wherever your package installer put Mercurial. + + + + <literal role="hg-ext">acl</literal>&emdash;access + control for parts of a repository + + The acl extension lets + you control which remote users are allowed to push changesets + to a networked server. You can protect any portion of a + repository (including the entire repo), so that a specific + remote user can push changes that do not affect the protected + portion. + + + This extension implements access control based on the + identity of the user performing a push, + not on who committed the changesets + they're pushing. It makes sense to use this hook only if you + have a locked-down server environment that authenticates + remote users, and you want to be sure that only specific users + are allowed to push changes to that server. + + + + Configuring the <literal role="hook">acl</literal> + hook + + In order to manage incoming changesets, the acl hook must be used as a + pretxnchangegroup hook. This + lets it see which files are modified by each incoming + changeset, and roll back a group of changesets if they + modify forbidden files. Example: + + [hooks] +pretxnchangegroup.acl = python:hgext.acl.hook + + The acl extension is + configured using three sections. + + + The acl section has + only one entry, sources, + which lists the sources of incoming changesets that the hook + should pay attention to. You don't normally need to + configure this section. + + + serve: + Control incoming changesets that are arriving from a + remote repository over http or ssh. This is the default + value of sources, and + usually the only setting you'll need for this + configuration item. + + + pull: + Control incoming changesets that are arriving via a pull + from a local repository. + + + push: + Control incoming changesets that are arriving via a push + from a local repository. + + + bundle: + Control incoming changesets that are arriving from + another repository via a bundle. + + + + The acl.allow + section controls the users that are allowed to add + changesets to the repository. If this section is not + present, all users that are not explicitly denied are + allowed. If this section is present, all users that are not + explicitly allowed are denied (so an empty section means + that all users are denied). + + + The acl.deny + section determines which users are denied from adding + changesets to the repository. If this section is not + present or is empty, no users are denied. + + + The syntaxes for the acl.allow and acl.deny sections are + identical. On the left of each entry is a glob pattern that + matches files or directories, relative to the root of the + repository; on the right, a user name. + + + In the following example, the user + docwriter can only push changes to the + docs subtree of the + repository, while intern can push changes + to any file or directory except source/sensitive. + + [acl.allow] +docs/** = docwriter +[acl.deny] +source/sensitive/** = intern + + + + Testing and troubleshooting + + If you want to test the acl hook, run it with Mercurial's + debugging output enabled. Since you'll probably be running + it on a server where it's not convenient (or sometimes + possible) to pass in the option, don't forget + that you can enable debugging output in your ~/.hgrc: + + [ui] +debug = true + With this enabled, the acl hook will print enough + information to let you figure out why it is allowing or + forbidding pushes from specific users. + + + + + + <literal + role="hg-ext">bugzilla</literal>&emdash;integration with + Bugzilla + + The bugzilla extension + adds a comment to a Bugzilla bug whenever it finds a reference + to that bug ID in a commit comment. You can install this hook + on a shared server, so that any time a remote user pushes + changes to this server, the hook gets run. + + + It adds a comment to the bug that looks like this (you can + configure the contents of the comment&emdash;see below): + + Changeset aad8b264143a, made by Joe User + <joe.user@domain.com> in the frobnitz repository, refers + to this bug. For complete details, see + http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a + Changeset description: Fix bug 10483 by guarding against some + NULL pointers + The value of this hook is that it automates the process of + updating a bug any time a changeset refers to it. If you + configure the hook properly, it makes it easy for people to + browse straight from a Bugzilla bug to a changeset that refers + to that bug. + + + You can use the code in this hook as a starting point for + some more exotic Bugzilla integration recipes. Here are a few + possibilities: + + + Require that every changeset pushed to the + server have a valid bug ID in its commit comment. In this + case, you'd want to configure the hook as a pretxncommit hook. This would + allow the hook to reject changes that didn't contain bug + IDs. + + + Allow incoming changesets to automatically + modify the state of a bug, as well as + simply adding a comment. For example, the hook could + recognise the string fixed bug 31337 as + indicating that it should update the state of bug 31337 to + requires testing. + + + + + Configuring the <literal role="hook">bugzilla</literal> + hook + + You should configure this hook in your server's + ~/.hgrc as an incoming hook, for example as + follows: + + [hooks] +incoming.bugzilla = python:hgext.bugzilla.hook + + Because of the specialised nature of this hook, and + because Bugzilla was not written with this kind of + integration in mind, configuring this hook is a somewhat + involved process. + + + Before you begin, you must install the MySQL bindings + for Python on the host(s) where you'll be running the hook. + If this is not available as a binary package for your + system, you can download it from + web:mysql-python. + + + Configuration information for this hook lives in the + bugzilla section of + your ~/.hgrc. + + + version: The version + of Bugzilla installed on the server. The database + schema that Bugzilla uses changes occasionally, so this + hook has to know exactly which schema to use. At the + moment, the only version supported is + 2.16. + + + host: + The hostname of the MySQL server that stores your + Bugzilla data. The database must be configured to allow + connections from whatever host you are running the + bugzilla hook on. + + + user: + The username with which to connect to the MySQL server. + The database must be configured to allow this user to + connect from whatever host you are running the bugzilla hook on. This user + must be able to access and modify Bugzilla tables. The + default value of this item is bugs, + which is the standard name of the Bugzilla user in a + MySQL database. + + + password: The MySQL + password for the user you configured above. This is + stored as plain text, so you should make sure that + unauthorised users cannot read the ~/.hgrc file where you + store this information. + + + db: + The name of the Bugzilla database on the MySQL server. + The default value of this item is + bugs, which is the standard name of + the MySQL database where Bugzilla stores its data. + + + notify: If you want + Bugzilla to send out a notification email to subscribers + after this hook has added a comment to a bug, you will + need this hook to run a command whenever it updates the + database. The command to run depends on where you have + installed Bugzilla, but it will typically look something + like this, if you have Bugzilla installed in /var/www/html/bugzilla: + + cd /var/www/html/bugzilla && + ./processmail %s nobody@nowhere.com + + The Bugzilla + processmail program expects to be + given a bug ID (the hook replaces + %s with the bug ID) + and an email address. It also expects to be able to + write to some files in the directory that it runs in. + If Bugzilla and this hook are not installed on the same + machine, you will need to find a way to run + processmail on the server where + Bugzilla is installed. + + + + + + Mapping committer names to Bugzilla user names + + By default, the bugzilla hook tries to use the + email address of a changeset's committer as the Bugzilla + user name with which to update a bug. If this does not suit + your needs, you can map committer email addresses to + Bugzilla user names using a usermap section. + + + Each item in the usermap section contains an + email address on the left, and a Bugzilla user name on the + right. + + [usermap] +jane.user@example.com = jane + You can either keep the usermap data in a normal + ~/.hgrc, or tell the + bugzilla hook to read the + information from an external usermap + file. In the latter case, you can store + usermap data by itself in (for example) + a user-modifiable repository. This makes it possible to let + your users maintain their own usermap entries. The main + ~/.hgrc file might look + like this: + + # regular hgrc file refers to external usermap file +[bugzilla] +usermap = /home/hg/repos/userdata/bugzilla-usermap.conf + While the usermap file that it + refers to might look like this: + + # bugzilla-usermap.conf - inside a hg repository +[usermap] stephanie@example.com = steph + + + + Configuring the text that gets added to a bug + + You can configure the text that this hook adds as a + comment; you specify it in the form of a Mercurial template. + Several ~/.hgrc entries + (still in the bugzilla + section) control this behavior. + + + strip: The number of + leading path elements to strip from a repository's path + name to construct a partial path for a URL. For example, + if the repositories on your server live under /home/hg/repos, and you + have a repository whose path is /home/hg/repos/app/tests, + then setting strip to + 4 will give a partial path of + app/tests. The + hook will make this partial path available when + expanding a template, as webroot. + + + template: The text of the + template to use. In addition to the usual + changeset-related variables, this template can use + hgweb (the value of the + hgweb configuration item above) and + webroot (the path constructed using + strip above). + + + + In addition, you can add a baseurl item to the web section of your ~/.hgrc. The bugzilla hook will make this + available when expanding a template, as the base string to + use when constructing a URL that will let users browse from + a Bugzilla comment to view a changeset. Example: + + [web] +baseurl = http://hg.domain.com/ + + Here is an example set of bugzilla hook config information. + + + &ch10-bugzilla-config.lst; + + + + Testing and troubleshooting + + The most common problems with configuring the bugzilla hook relate to running + Bugzilla's processmail script and + mapping committer names to user names. + + + Recall from above that the user + that runs the Mercurial process on the server is also the + one that will run the processmail + script. The processmail script + sometimes causes Bugzilla to write to files in its + configuration directory, and Bugzilla's configuration files + are usually owned by the user that your web server runs + under. + + + You can cause processmail to be run + with the suitable user's identity using the + sudo command. Here is an example entry + for a sudoers file. + + hg_user = (httpd_user) +NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s + This allows the hg_user user to run a + processmail-wrapper program under the + identity of httpd_user. + + + This indirection through a wrapper script is necessary, + because processmail expects to be run + with its current directory set to wherever you installed + Bugzilla; you can't specify that kind of constraint in a + sudoers file. The contents of the + wrapper script are simple: + + #!/bin/sh +cd `dirname $0` && ./processmail "$1" nobody@example.com + It doesn't seem to matter what email address you pass to + processmail. + + + If your usermap is + not set up correctly, users will see an error message from + the bugzilla hook when they + push changes to the server. The error message will look + like this: + + cannot find bugzilla user id for john.q.public@example.com + What this means is that the committer's address, + john.q.public@example.com, is not a valid + Bugzilla user name, nor does it have an entry in your + usermap that maps it to + a valid Bugzilla user name. + + + + + + <literal role="hg-ext">notify</literal>&emdash;send email + notifications + + Although Mercurial's built-in web server provides RSS + feeds of changes in every repository, many people prefer to + receive change notifications via email. The notify hook lets you send out + notifications to a set of email addresses whenever changesets + arrive that those subscribers are interested in. + + + As with the bugzilla + hook, the notify hook is + template-driven, so you can customise the contents of the + notification messages that it sends. + + + By default, the notify + hook includes a diff of every changeset that it sends out; you + can limit the size of the diff, or turn this feature off + entirely. It is useful for letting subscribers review changes + immediately, rather than clicking to follow a URL. + + + + Configuring the <literal role="hg-ext">notify</literal> + hook + + You can set up the notify hook to send one email + message per incoming changeset, or one per incoming group of + changesets (all those that arrived in a single pull or + push). + + [hooks] +# send one email per group of changes +changegroup.notify = python:hgext.notify.hook +# send one email per change +incoming.notify = python:hgext.notify.hook + + Configuration information for this hook lives in the + notify section of a + ~/.hgrc file. + + + test: + By default, this hook does not send out email at all; + instead, it prints the message that it + would send. Set this item to + false to allow email to be sent. The + reason that sending of email is turned off by default is + that it takes several tries to configure this extension + exactly as you would like, and it would be bad form to + spam subscribers with a number of broken + notifications while you debug your configuration. + + + config: + The path to a configuration file that contains + subscription information. This is kept separate from + the main ~/.hgrc so + that you can maintain it in a repository of its own. + People can then clone that repository, update their + subscriptions, and push the changes back to your server. + + + strip: + The number of leading path separator characters to strip + from a repository's path, when deciding whether a + repository has subscribers. For example, if the + repositories on your server live in /home/hg/repos, and + notify is considering a + repository named /home/hg/repos/shared/test, + setting strip to + 4 will cause notify to trim the path it + considers down to shared/test, and it will + match subscribers against that. + + + template: The template + text to use when sending messages. This specifies both + the contents of the message header and its body. + + + maxdiff: The maximum + number of lines of diff data to append to the end of a + message. If a diff is longer than this, it is + truncated. By default, this is set to 300. Set this to + 0 to omit diffs from notification + emails. + + + sources: A list of + sources of changesets to consider. This lets you limit + notify to only sending + out email about changes that remote users pushed into + this repository via a server, for example. See + for the sources you + can specify here. + + + + If you set the baseurl + item in the web section, + you can use it in a template; it will be available as + webroot. + + + Here is an example set of notify configuration information. + + + &ch10-notify-config.lst; + + This will produce a message that looks like the + following: + + + &ch10-notify-config-mail.lst; + + + + Testing and troubleshooting + + Do not forget that by default, the notify extension will not + send any mail until you explicitly configure it to do so, + by setting test to + false. Until you do that, it simply + prints the message it would send. + + + + + + + Information for writers of hooks + + + In-process hook execution + + An in-process hook is called with arguments of the + following form: + + def myhook(ui, repo, **kwargs): pass + The ui parameter is a ui object. The + repo parameter is a localrepository + object. The names and values of the + **kwargs parameters depend on the hook + being invoked, with the following common features: + + + If a parameter is named + node or parentN, it + will contain a hexadecimal changeset ID. The empty string + is used to represent null changeset ID + instead of a string of zeroes. + + + If a parameter is named + url, it will contain the URL of a + remote repository, if that can be determined. + + + Boolean-valued parameters are represented as + Python bool objects. + + + + An in-process hook is called without a change to the + process's working directory (unlike external hooks, which are + run in the root of the repository). It must not change the + process's working directory, or it will cause any calls it + makes into the Mercurial API to fail. + + + If a hook returns a boolean false value, it + is considered to have succeeded. If it returns a boolean + true value or raises an exception, it is + considered to have failed. A useful way to think of the + calling convention is tell me if you fail. + + + Note that changeset IDs are passed into Python hooks as + hexadecimal strings, not the binary hashes that Mercurial's + APIs normally use. To convert a hash from hex to binary, use + the bin function. + + + + + External hook execution + + An external hook is passed to the shell of the user + running Mercurial. Features of that shell, such as variable + substitution and command redirection, are available. The hook + is run in the root directory of the repository (unlike + in-process hooks, which are run in the same directory that + Mercurial was run in). + + + Hook parameters are passed to the hook as environment + variables. Each environment variable's name is converted in + upper case and prefixed with the string + HG_. For example, if the + name of a parameter is node, + the name of the environment variable representing that + parameter will be HG_NODE. + + + A boolean parameter is represented as the string + 1 for true, + 0 for false. + If an environment variable is named HG_NODE, + HG_PARENT1 or HG_PARENT2, it + contains a changeset ID represented as a hexadecimal string. + The empty string is used to represent null changeset + ID instead of a string of zeroes. If an environment + variable is named HG_URL, it will contain the + URL of a remote repository, if that can be determined. + + + If a hook exits with a status of zero, it is considered to + have succeeded. If it exits with a non-zero status, it is + considered to have failed. + + + + + Finding out where changesets come from + + A hook that involves the transfer of changesets between a + local repository and another may be able to find out + information about the far side. Mercurial + knows how changes are being transferred, + and in many cases where they are being + transferred to or from. + + + + Sources of changesets + + Mercurial will tell a hook what means are, or were, used + to transfer changesets between repositories. This is + provided by Mercurial in a Python parameter named + source, or an environment variable named + HG_SOURCE. + + + + serve: Changesets are + transferred to or from a remote repository over http or + ssh. + + + pull: Changesets are + being transferred via a pull from one repository into + another. + + + push: Changesets are + being transferred via a push from one repository into + another. + + + bundle: Changesets are + being transferred to or from a bundle. + + + + + + Where changes are going&emdash;remote repository + URLs + + When possible, Mercurial will tell a hook the location + of the far side of an activity that transfers + changeset data between repositories. This is provided by + Mercurial in a Python parameter named + url, or an environment variable named + HG_URL. + + + This information is not always known. If a hook is + invoked in a repository that is being served via http or + ssh, Mercurial cannot tell where the remote repository is, + but it may know where the client is connecting from. In + such cases, the URL will take one of the following forms: + + + remote:ssh:1.2.3.4&emdash;remote + ssh client, at the IP address + 1.2.3.4. + + + remote:http:1.2.3.4&emdash;remote + http client, at the IP address + 1.2.3.4. If the client is using SSL, + this will be of the form + remote:https:1.2.3.4. + + + Empty&emdash;no information could be + discovered about the remote client. + + + + + + + + Hook reference + + + <literal role="hook">changegroup</literal>&emdash;after + remote changesets added + + This hook is run after a group of pre-existing changesets + has been added to the repository, for example via a hg pull or hg + unbundle. This hook is run once per operation + that added one or more changesets. This is in contrast to the + incoming hook, which is run + once per changeset, regardless of whether the changesets + arrive in a group. + + + Some possible uses for this hook include kicking off an + automated build or test of the added changesets, updating a + bug database, or notifying subscribers that a repository + contains new changes. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + tip, inclusive, were added by a single + hg pull, hg push or hg unbundle. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: incoming (), prechangegroup (), pretxnchangegroup () + + + + + <literal role="hook">commit</literal>&emdash;after a new + changeset is created + + This hook is run after a new changeset has been created. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the newly committed changeset. + + + parent1: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + + + parent2: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + + + + See also: precommit (), pretxncommit () + + + + + <literal role="hook">incoming</literal>&emdash;after one + remote changeset is added + + This hook is run after a pre-existing changeset has been + added to the repository, for example via a hg push. If a group of changesets + was added in a single operation, this hook is called once for + each added changeset. + + + You can use this hook for the same purposes as + the changegroup hook (); it's simply more + convenient sometimes to run a hook once per group of + changesets, while other times it's handier once per changeset. + + + Parameters to this hook: + + + node: A changeset ID. The + ID of the newly added changeset. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup () prechangegroup (), pretxnchangegroup () + + + + + <literal role="hook">outgoing</literal>&emdash;after + changesets are propagated + + This hook is run after a group of changesets has been + propagated out of this repository, for example by a hg push or hg + bundle command. + + + One possible use for this hook is to notify administrators + that changes have been pulled. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset of the group that was + sent. + + + source: A string. The + source of the of the operation (see ). If a remote + client pulled changes from this repository, + source will be + serve. If the client that obtained + changes from this repository was local, + source will be + bundle, pull, or + push, depending on the operation the + client performed. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: preoutgoing () + + + + + <literal + role="hook">prechangegroup</literal>&emdash;before starting + to add remote changesets + + This controlling hook is run before Mercurial begins to + add a group of changesets from another repository. + + + This hook does not have any information about the + changesets to be added, because it is run before transmission + of those changesets is allowed to begin. If this hook fails, + the changesets will not be transmitted. + + + One use for this hook is to prevent external changes from + being added to a repository. For example, you could use this + to freeze a server-hosted branch temporarily or + permanently so that users cannot push to it, while still + allowing a local administrator to modify the repository. + + + Parameters to this hook: + + + source: A string. The + source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup (), incoming (), pretxnchangegroup () + + + + + <literal role="hook">precommit</literal>&emdash;before + starting to commit a changeset + + This hook is run before Mercurial begins to commit a new + changeset. It is run before Mercurial has any of the metadata + for the commit, such as the files to be committed, the commit + message, or the commit date. + + + One use for this hook is to disable the ability to commit + new changesets, while still allowing incoming changesets. + Another is to run a build or test, and only allow the commit + to begin if the build or test succeeds. + + + Parameters to this hook: + + + parent1: A changeset ID. + The changeset ID of the first parent of the working + directory. + + + parent2: A changeset ID. + The changeset ID of the second parent of the working + directory. + + + If the commit proceeds, the parents of the working + directory will become the parents of the new changeset. + + + See also: commit + (), pretxncommit () + + + + + <literal role="hook">preoutgoing</literal>&emdash;before + starting to propagate changesets + + This hook is invoked before Mercurial knows the identities + of the changesets to be transmitted. + + + One use for this hook is to prevent changes from being + transmitted to another repository. + + + Parameters to this hook: + + + source: A + string. The source of the operation that is attempting to + obtain changes from this repository (see ). See the documentation + for the source parameter to the + outgoing hook, in + , for possible values + of this parameter. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: outgoing () + + + + + <literal role="hook">pretag</literal>&emdash;before + tagging a changeset + + This controlling hook is run before a tag is created. If + the hook succeeds, creation of the tag proceeds. If the hook + fails, the tag is not created. + + + Parameters to this hook: + + + local: A boolean. Whether + the tag is local to this repository instance (i.e. stored + in .hg/localtags) or + managed by Mercurial (stored in .hgtags). + + + node: A changeset ID. The + ID of the changeset to be tagged. + + + tag: A string. The name of + the tag to be created. + + + + If the tag to be created is + revision-controlled, the precommit and pretxncommit hooks ( and ) will also be run. + + + See also: tag + () + + + + <literal + role="hook">pretxnchangegroup</literal>&emdash;before + completing addition of remote changesets + + This controlling hook is run before a + transaction&emdash;that manages the addition of a group of new + changesets from outside the repository&emdash;completes. If + the hook succeeds, the transaction completes, and all of the + changesets become permanent within this repository. If the + hook fails, the transaction is rolled back, and the data for + the changesets is erased. + + + This hook can access the metadata associated with the + almost-added changesets, but it should not do anything + permanent with this data. It must also not modify the working + directory. + + + While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-added changesets as if they are permanent. This may + lead to race conditions if you do not take steps to avoid + them. + + + This hook can be used to automatically vet a group of + changesets. If the hook fails, all of the changesets are + rejected when the transaction rolls back. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + tip, + inclusive, were added by a single hg pull, hg push or hg unbundle. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup (), incoming (), prechangegroup () + + + + + <literal role="hook">pretxncommit</literal>&emdash;before + completing commit of new changeset + + This controlling hook is run before a + transaction&emdash;that manages a new commit&emdash;completes. + If the hook succeeds, the transaction completes and the + changeset becomes permanent within this repository. If the + hook fails, the transaction is rolled back, and the commit + data is erased. + + + This hook can access the metadata associated with the + almost-new changeset, but it should not do anything permanent + with this data. It must also not modify the working + directory. + + + While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-new changeset as if it is permanent. This may lead to + race conditions if you do not take steps to avoid them. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the newly committed changeset. + + + parent1: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + + + parent2: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + + + + See also: precommit () + + + + + <literal role="hook">preupdate</literal>&emdash;before + updating or merging working directory + + This controlling hook is run before an update + or merge of the working directory begins. It is run only if + Mercurial's normal pre-update checks determine that the update + or merge can proceed. If the hook succeeds, the update or + merge may proceed; if it fails, the update or merge does not + start. + + + Parameters to this hook: + + + parent1: A + changeset ID. The ID of the parent that the working + directory is to be updated to. If the working directory + is being merged, it will not change this parent. + + + parent2: A + changeset ID. Only set if the working directory is being + merged. The ID of the revision that the working directory + is being merged with. + + + + See also: update + () + + + + <literal role="hook">tag</literal>&emdash;after tagging a + changeset + + This hook is run after a tag has been created. + + + Parameters to this hook: + + + local: A boolean. Whether + the new tag is local to this repository instance (i.e. + stored in .hg/localtags) or managed by + Mercurial (stored in .hgtags). + + + node: A changeset ID. The + ID of the changeset that was tagged. + + + tag: A string. The name of + the tag that was created. + + + + If the created tag is revision-controlled, the commit hook (section ) is run before this hook. + + + See also: pretag + () + + + + + <literal role="hook">update</literal>&emdash;after + updating or merging working directory + + This hook is run after an update or merge of the working + directory completes. Since a merge can fail (if the external + hgmerge command fails to resolve conflicts + in a file), this hook communicates whether the update or merge + completed cleanly. + + + + error: A boolean. + Indicates whether the update or merge completed + successfully. + + + parent1: A changeset ID. + The ID of the parent that the working directory was + updated to. If the working directory was merged, it will + not have changed this parent. + + + parent2: A changeset ID. + Only set if the working directory was merged. The ID of + the revision that the working directory was merged with. + + + + See also: preupdate + () + + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch10-template.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch10-template.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,673 @@ + + + + + Customising the output of Mercurial + + Mercurial provides a powerful mechanism to let you control how + it displays information. The mechanism is based on templates. + You can use templates to generate specific output for a single + command, or to customise the entire appearance of the built-in web + interface. + + + Using precanned output styles + + 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&emdash;five lines of output per changeset. The + compact style reduces this to three lines, + presented in a sparse manner. + + &interaction.template.simple.compact; + + The changelog style hints at the + expressive power of Mercurial's templating engine. This style + attempts to follow the GNU Project's changelog + guidelinesweb:changelog. + + &interaction.template.simple.changelog; + + You will not be shocked to learn that Mercurial's default + output style is named default. + + + Setting a default style + + You can modify the output style that Mercurial will use + for every command by editing your ~/.hgrc file, naming the style + you would prefer to use. + + [ui] +style = compact + + If you write a style of your own, you can use it by either + providing the path to your style file, or copying your style + file into a location where Mercurial can find it (typically + the templates subdirectory of your + Mercurial install directory). + + + + + Commands that support styles and templates + + All of Mercurial's + log-like commands let you use + styles and templates: hg + incoming, hg log, + hg outgoing, and hg tip. + + As I write this manual, these are so far the only commands + that support styles and templates. Since these are the most + important commands that need customisable output, there has been + little pressure from the Mercurial user community to add style + and template support to other commands. + + + + The basics of templating + + At its simplest, a Mercurial template is a piece of text. + Some of the text never changes, while other parts are + expanded, or replaced with new text, when + necessary. + + Before we continue, let's look again at a simple example of + Mercurial's normal output. + + &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 option to the hg log command tells Mercurial to use + the given text as the template when printing each + changeset. + + Notice that the template string above ends with the text + \n. This is an + escape sequence, telling Mercurial to print + a newline at the end of each template item. If you omit this + newline, Mercurial will run each piece of output together. See + 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 + {desc} in the template has + been replaced in the output with the description of each + changeset. Every time Mercurial finds text enclosed in curly + braces ({ and + }), 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 + . + + + + Common template keywords + + You can start writing simple templates immediately using the + keywords below. + + + author: String. The + unmodified author of the changeset. + + branches: String. The + name of the branch on which the changeset was committed. + Will be empty if the branch name was + default. + + date: + Date information. The date when the changeset was + committed. This is not human-readable; + you must pass it through a filter that will render it + appropriately. See for more information + on filters. The date is expressed as a pair of numbers. The + first number is a Unix UTC timestamp (seconds since January + 1, 1970); the second is the offset of the committer's + timezone from UTC, in seconds. + + desc: + String. The text of the changeset description. + + files: List of strings. + All files modified, added, or removed by this + changeset. + + file_adds: List of + strings. Files added by this changeset. + + file_dels: List of + strings. Files removed by this changeset. + + node: + String. The changeset identification hash, as a + 40-character hexadecimal string. + + parents: List of + strings. The parents of the changeset. + + rev: + Integer. The repository-local changeset revision + number. + + tags: + List of strings. Any tags associated with the + changeset. + + + A few simple experiments will show us what to expect when we + use these keywords; you can see the results below. + +&interaction.template.simple.keywords; + + As we noted above, the date keyword does not produce + human-readable output, so we must treat it specially. This + involves using a filter, about which more + in . + + &interaction.template.simple.datekeyword; + + + + Escape sequences + + Mercurial's templating engine recognises the most commonly + used escape sequences in strings. When it sees a backslash + (\) character, it looks at the + following character and substitutes the two characters with a + single replacement, as described below. + + + \: + Backslash, \, ASCII + 134. + + \n: Newline, + ASCII 12. + + \r: Carriage + return, ASCII 15. + + \t: Tab, ASCII + 11. + + \v: Vertical + tab, ASCII 13. + + {: Open curly + brace, {, ASCII + 173. + + }: Close curly + brace, }, ASCII + 175. + + + As indicated above, if you want the expansion of a template + to contain a literal \, + {, or + { character, you must escape + it. + + + + Filtering keywords to change their results + + Some of the results of template expansion are not + immediately easy to use. Mercurial lets you specify an optional + chain of filters to modify the result of + expanding a keyword. You have already seen a common filter, + isodate, in + action above, to make a date readable. + + Below is a list of the most commonly used filters that + Mercurial supports. While some filters can be applied to any + text, others can only be used in specific circumstances. The + name of each filter is followed first by an indication of where + it can be used, then a description of its effect. + + + addbreaks: Any text. Add + an XHTML <br/> tag + before the end of every line except the last. For example, + foo\nbar becomes + foo<br/>\nbar. + + age: date keyword. Render + the age of the date, relative to the current time. Yields a + string like 10 + minutes. + + basename: Any text, but + most useful for the files keyword and its + relatives. Treat the text as a path, and return the + basename. For example, + foo/bar/baz becomes + baz. + + date: date keyword. Render a + date in a similar format to the Unix date command, but with + timezone included. Yields a string like Mon + Sep 04 15:13:13 2006 -0700. + + domain: Any text, + but most useful for the author keyword. Finds + the first string that looks like an email address, and + extract just the domain component. For example, + Bryan O'Sullivan + <bos@serpentine.com> becomes + serpentine.com. + + email: Any text, + but most useful for the author keyword. Extract + the first string that looks like an email address. For + example, Bryan O'Sullivan + <bos@serpentine.com> becomes + bos@serpentine.com. + + escape: Any text. + Replace the special XML/XHTML characters + &, + < and + > with XML + entities. + + fill68: Any text. Wrap + the text to fit in 68 columns. This is useful before you + pass text through the tabindent filter, and + still want it to fit in an 80-column fixed-font + window. + + fill76: Any text. Wrap + the text to fit in 76 columns. + + firstline: Any text. + Yield the first line of text, without any trailing + newlines. + + hgdate: date keyword. Render + the date as a pair of readable numbers. Yields a string + like 1157407993 + 25200. + + isodate: date keyword. Render + the date as a text string in ISO 8601 format. Yields a + string like 2006-09-04 15:13:13 + -0700. + + obfuscate: Any text, but + most useful for the author keyword. Yield + the input text rendered as a sequence of XML entities. This + helps to defeat some particularly stupid screen-scraping + email harvesting spambots. + + person: Any text, + but most useful for the author keyword. Yield + the text before an email address. For example, + Bryan O'Sullivan + <bos@serpentine.com> becomes + Bryan O'Sullivan. + + rfc822date: + date keyword. + Render a date using the same format used in email headers. + Yields a string like Mon, 04 Sep 2006 + 15:13:13 -0700. + + short: Changeset + hash. Yield the short form of a changeset hash, i.e. a + 12-character hexadecimal string. + + shortdate: date keyword. Render + the year, month, and day of the date. Yields a string like + 2006-09-04. + + strip: + Any text. Strip all leading and trailing whitespace from + the string. + + tabindent: Any text. + Yield the text, with every line except the first starting + with a tab character. + + urlescape: Any text. + Escape all characters that are considered + special by URL parsers. For example, + foo bar becomes + foo%20bar. + + user: Any text, + but most useful for the author keyword. Return + the user portion of an email address. For + example, Bryan O'Sullivan + <bos@serpentine.com> becomes + bos. + + +&interaction.template.simple.manyfilters; + + + If you try to apply a filter to a piece of data that it + cannot process, Mercurial will fail and print a Python + exception. For example, trying to run the output of the + desc keyword into + the isodate + filter is not a good idea. + + + + Combining filters + + It is easy to combine filters to yield output in the form + you would like. The following chain of filters tidies up a + description, then makes sure that it fits cleanly into 68 + columns, then indents it by a further 8 characters (at least + on Unix-like systems, where a tab is conventionally 8 + characters wide). + + &interaction.template.simple.combine; + + Note the use of \t (a + tab character) in the template to force the first line to be + indented; this is necessary since tabindent indents all + lines except the first. + + Keep in mind that the order of filters in a chain is + significant. The first filter is applied to the result of the + keyword; the second to the result of the first filter; and so + on. For example, using fill68|tabindent + gives very different results from + tabindent|fill68. + + + + + + From templates to styles + + A command line template provides a quick and simple way to + format some output. Templates can become verbose, though, and + it's useful to be able to give a template a name. A style file + is a template with a name, stored in a file. + + More than that, using a style file unlocks the power of + Mercurial's templating engine in ways that are not possible + using the command line option. + + + The simplest of style files + + Our simple style file contains just one line: + + &interaction.template.simple.rev; + + This tells Mercurial, if you're printing a + changeset, use the text on the right as the + template. + + + + Style file syntax + + The syntax rules for a style file are simple. + + + The file is processed one line at a + time. + + Leading and trailing white space are + ignored. + + Empty lines are skipped. + + If a line starts with either of the characters + # or + ;, the entire line is + treated as a comment, and skipped as if empty. + + A line starts with a keyword. This must start + with an alphabetic character or underscore, and can + subsequently contain any alphanumeric character or + underscore. (In regexp notation, a keyword must match + [A-Za-z_][A-Za-z0-9_]*.) + + The next element must be an + = character, which can + be preceded or followed by an arbitrary amount of white + space. + + If the rest of the line starts and ends with + matching quote characters (either single or double quote), + it is treated as a template body. + + If the rest of the line does + not start with a quote character, it is + treated as the name of a file; the contents of this file + will be read and used as a template body. + + + + + + Style files by example + + To illustrate how to write a style file, we will construct a + few by example. Rather than provide a complete style file and + walk through it, we'll mirror the usual process of developing a + style file by starting with something very simple, and walking + through a series of successively more complete examples. + + + Identifying mistakes in style files + + If Mercurial encounters a problem in a style file you are + working on, it prints a terse error message that, once you + figure out what it means, is actually quite useful. + +&interaction.template.svnstyle.syntax.input; + + Notice that broken.style attempts to + define a changeset keyword, but forgets to + give any content for it. When instructed to use this style + file, Mercurial promptly complains. + + &interaction.template.svnstyle.syntax.error; + + This error message looks intimidating, but it is not too + hard to follow. + + + The first component is simply Mercurial's way + of saying I am giving up. + ___abort___: broken.style:1: parse error + + Next comes the name of the style file that + contains the error. + abort: ___broken.style___:1: parse error + + Following the file name is the line number + where the error was encountered. + abort: broken.style:___1___: parse error + + Finally, a description of what went + wrong. + abort: broken.style:1: ___parse error___ + + The description of the problem is not always + clear (as in this case), but even when it is cryptic, it + is almost always trivial to visually inspect the offending + line in the style file and see what is wrong. + + + + + Uniquely identifying a repository + + If you would like to be able to identify a Mercurial + repository fairly uniquely using a short string + as an identifier, you can use the first revision in the + repository. + + &interaction.template.svnstyle.id; + + This is not guaranteed to be unique, but it is + nevertheless useful in many cases. + + It will not work in a completely empty + repository, because such a repository does not have a + revision zero. + + Neither will it work in the (extremely rare) + case where a repository is a merge of two or more formerly + independent repositories, and you still have those + repositories around. + + Here are some uses to which you could put this + identifier: + + As a key into a table for a database that + manages repositories on a server. + + As half of a {repository + ID, revision ID} tuple. + Save this information away when you run an automated build + or other activity, so that you can replay + the build later if necessary. + + + + + Mimicking Subversion's output + + Let's try to emulate the default output format used by + another revision control tool, Subversion. + + &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. + + Subversion prints a readable + date (the Wed, 27 Sep 2006 in the + example output above) in parentheses. Mercurial's + templating engine does not provide a way to display a date + in this format without also printing the time and time + zone. + + We emulate Subversion's printing of + separator lines full of + - characters by ending + the template with such a line. We use the templating + engine's header + keyword to print a separator line as the first line of + output (see below), thus achieving similar output to + Subversion. + + Subversion's output includes a count in the + header of the number of lines in the commit message. We + cannot replicate this in Mercurial; the templating engine + does not currently provide a filter that counts the number + of lines the template generates. + + It took me no more than a minute or two of work to replace + literal text from an example of Subversion's output with some + keywords and filters to give the template above. The style + file simply refers to the template. + + &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 + \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. + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch11-mq.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch11-mq.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1321 @@ + + + + + Managing change with Mercurial Queues + + + The patch management problem + + Here is a common scenario: you need to install a software + package from source, but you find a bug that you must fix in the + source before you can start using the package. You make your + changes, forget about the package for a while, and a few months + later you need to upgrade to a newer version of the package. If + the newer version of the package still has the bug, you must + extract your fix from the older source tree and apply it against + the newer version. This is a tedious task, and it's easy to + make mistakes. + + This is a simple case of the patch management + problem. You have an upstream source tree that + you can't change; you need to make some local changes on top of + the upstream tree; and you'd like to be able to keep those + changes separate, so that you can apply them to newer versions + of the upstream source. + + The patch management problem arises in many situations. + Probably the most visible is that a user of an open source + software project will contribute a bug fix or new feature to the + project's maintainers in the form of a patch. + + Distributors of operating systems that include open source + software often need to make changes to the packages they + distribute so that they will build properly in their + environments. + + When you have few changes to maintain, it is easy to manage + a single patch using the standard diff and + patch programs (see for a discussion of these + tools). Once the number of changes grows, it starts to make + sense to maintain patches as discrete chunks of + work, so that for example a single patch will contain + only one bug fix (the patch might modify several files, but it's + doing only one thing), and you may have a number + of such patches for different bugs you need fixed and local + changes you require. In this situation, if you submit a bug fix + patch to the upstream maintainers of a package and they include + your fix in a subsequent release, you can simply drop that + single patch when you're updating to the newer release. + + Maintaining a single patch against an upstream tree is a + little tedious and error-prone, but not difficult. However, the + complexity of the problem grows rapidly as the number of patches + you have to maintain increases. With more than a tiny number of + patches in hand, understanding which ones you have applied and + maintaining them moves from messy to overwhelming. + + Fortunately, Mercurial includes a powerful extension, + Mercurial Queues (or simply MQ), that massively + simplifies the patch management problem. + + + + The prehistory of Mercurial Queues + + During the late 1990s, several Linux kernel developers + started to maintain patch series that modified + the behavior of the Linux kernel. Some of these series were + focused on stability, some on feature coverage, and others were + more speculative. + + The sizes of these patch series grew rapidly. In 2002, + Andrew Morton published some shell scripts he had been using to + automate the task of managing his patch queues. Andrew was + successfully using these scripts to manage hundreds (sometimes + thousands) of patches on top of the Linux kernel. + + + A patchwork quilt + + In early 2003, Andreas Gruenbacher and Martin Quinson + borrowed the approach of Andrew's scripts and published a tool + called patchwork quilt + web:quilt, or simply quilt + (see gruenbacher:2005 for a paper + describing it). Because quilt substantially automated patch + management, it rapidly gained a large following among open + source software developers. + + Quilt manages a stack of patches on + top of a directory tree. To begin, you tell quilt to manage a + directory tree, and tell it which files you want to manage; it + stores away the names and contents of those files. To fix a + bug, you create a new patch (using a single command), edit the + files you need to fix, then refresh the + patch. + + The refresh step causes quilt to scan the directory tree; + it updates the patch with all of the changes you have made. + You can create another patch on top of the first, which will + track the changes required to modify the tree from tree + with one patch applied to tree with two + patches applied. + + You can change which patches are + applied to the tree. If you pop a patch, the + changes made by that patch will vanish from the directory + tree. Quilt remembers which patches you have popped, though, + so you can push a popped patch again, and the + directory tree will be restored to contain the modifications + in the patch. Most importantly, you can run the + refresh command at any time, and the topmost + applied patch will be updated. This means that you can, at + any time, change both which patches are applied and what + modifications those patches make. + + Quilt knows nothing about revision control tools, so it + works equally well on top of an unpacked tarball or a + Subversion working copy. + + + + From patchwork quilt to Mercurial Queues + + In mid-2005, Chris Mason took the features of quilt and + wrote an extension that he called Mercurial Queues, which + added quilt-like behavior to Mercurial. + + The key difference between quilt and MQ is that quilt + knows nothing about revision control systems, while MQ is + integrated into Mercurial. Each patch + that you push is represented as a Mercurial changeset. Pop a + patch, and the changeset goes away. + + Because quilt does not care about revision control tools, + it is still a tremendously useful piece of software to know + about for situations where you cannot use Mercurial and + MQ. + + + + + The huge advantage of MQ + + I cannot overstate the value that MQ offers through the + unification of patches and revision control. + + A major reason that patches have persisted in the free + software and open source world&emdash;in spite of the + availability of increasingly capable revision control tools over + the years&emdash;is the agility they + offer. + + Traditional revision control tools make a permanent, + irreversible record of everything that you do. While this has + great value, it's also somewhat stifling. If you want to + perform a wild-eyed experiment, you have to be careful in how + you go about it, or you risk leaving unneeded&emdash;or worse, + misleading or destabilising&emdash;traces of your missteps and + errors in the permanent revision record. + + By contrast, MQ's marriage of distributed revision control + with patches makes it much easier to isolate your work. Your + patches live on top of normal revision history, and you can make + them disappear or reappear at will. If you don't like a patch, + you can drop it. If a patch isn't quite as you want it to be, + simply fix it&emdash;as many times as you need to, until you + have refined it into the form you desire. + + As an example, the integration of patches with revision + control makes understanding patches and debugging their + effects&emdash;and their interplay with the code they're based + on&emdash;enormously easier. Since every + applied patch has an associated changeset, you can give hg log a file name to see which + changesets and patches affected the file. You can use the + hg bisect command to + binary-search through all changesets and applied patches to see + where a bug got introduced or fixed. You can use the hg annotate command to see which + changeset or patch modified a particular line of a source file. + And so on. + + + + Understanding patches + + Because MQ doesn't hide its patch-oriented nature, it is + helpful to understand what patches are, and a little about the + tools that work with them. + + The traditional Unix diff command + compares two files, and prints a list of differences between + them. The patch command understands these + differences as modifications to make to a + file. Take a look below for a simple example of these commands + in action. + +&interaction.mq.dodiff.diff; + + The type of file that diff generates (and + patch takes as input) is called a + patch or a diff; there is no + difference between a patch and a diff. (We'll use the term + patch, since it's more commonly used.) + + A patch file can start with arbitrary text; the + patch command ignores this text, but MQ uses + it as the commit message when creating changesets. To find the + beginning of the patch content, patch + searches for the first line that starts with the string + diff -. + + MQ works with unified diffs + (patch can accept several other diff formats, + but MQ doesn't). A unified diff contains two kinds of header. + The file header describes the file being + modified; it contains the name of the file to modify. When + patch sees a new file header, it looks for a + file with that name to start modifying. + + After the file header comes a series of + hunks. Each hunk starts with a header; + this identifies the range of line numbers within the file that + the hunk should modify. Following the header, a hunk starts and + ends with a few (usually three) lines of text from the + unmodified file; these are called the + context for the hunk. If there's only a + small amount of context between successive hunks, + diff doesn't print a new hunk header; it just + runs the hunks together, with a few lines of context between + modifications. + + Each line of context begins with a space character. Within + the hunk, a line that begins with + - means remove this + line, while a line that begins with + + means insert this + line. For example, a line that is modified is + represented by one deletion and one insertion. + + We will return to some of the more subtle aspects of patches + later (in ), but you + should have + enough information now to use MQ. + + + + Getting started with Mercurial Queues + + Because MQ is implemented as an extension, you must + explicitly enable before you can use it. (You don't need to + download anything; MQ ships with the standard Mercurial + distribution.) To enable MQ, edit your ~/.hgrc file, and add the lines + below. + + [extensions] +hgext.mq = + + Once the extension is enabled, it will make a number of new + commands available. To verify that the extension is working, + you can use hg help to see if + the qinit command is now + available. + +&interaction.mq.qinit-help.help; + + You can use MQ with any Mercurial + repository, and its commands only operate within that + repository. To get started, simply prepare the repository using + the qinit command. + +&interaction.mq.tutorial.qinit; + + This command creates an empty directory called .hg/patches, where + MQ will keep its metadata. As with many Mercurial commands, the + qinit command prints nothing + if it succeeds. + + + Creating a new patch + + To begin work on a new patch, use the qnew command. This command takes + one argument, the name of the patch to create. + + MQ will use this as the name of an actual file in the + .hg/patches directory, as you + can see below. + +&interaction.mq.tutorial.qnew; + + Also newly present in the .hg/patches directory are two + other files, series and + status. The series file lists all of the + patches that MQ knows about for this repository, with one + patch per line. Mercurial uses the status file for internal + book-keeping; it tracks all of the patches that MQ has + applied in this repository. + + + You may sometimes want to edit the series file by hand; for + example, to change the sequence in which some patches are + applied. However, manually editing the status file is almost always a + bad idea, as it's easy to corrupt MQ's idea of what is + happening. + + + Once you have created your new patch, you can edit files + in the working directory as you usually would. All of the + normal Mercurial commands, such as hg + diff and hg + annotate, work exactly as they did before. + + + + Refreshing a patch + + When you reach a point where you want to save your work, + use the qrefresh command + to update the patch you are working on. + +&interaction.mq.tutorial.qrefresh; + + This command folds the changes you have made in the + working directory into your patch, and updates its + corresponding changeset to contain those changes. + + You can run qrefresh + as often as you like, so it's a good way to + checkpoint your work. Refresh your patch at an + opportune time; try an experiment; and if the experiment + doesn't work out, hg revert + your modifications back to the last time you refreshed. + +&interaction.mq.tutorial.qrefresh2; + + + + Stacking and tracking patches + + Once you have finished working on a patch, or need to work + on another, you can use the qnew command again to create a + new patch. Mercurial will apply this patch on top of your + existing patch. + +&interaction.mq.tutorial.qnew2; + Notice that the patch contains the changes in our prior + patch as part of its context (you can see this more clearly in + the output of hg + annotate). + + So far, with the exception of qnew and qrefresh, we've been careful to + only use regular Mercurial commands. However, MQ provides + many commands that are easier to use when you are thinking + about patches, as illustrated below. + +&interaction.mq.tutorial.qseries; + + + The qseries command lists every + patch that MQ knows about in this repository, from oldest + to newest (most recently + created). + + The qapplied command lists every + patch that MQ has applied in this + repository, again from oldest to newest (most recently + applied). + + + + + Manipulating the patch stack + + The previous discussion implied that there must be a + difference between known and + applied patches, and there is. MQ can manage a + patch without it being applied in the repository. + + An applied patch has a corresponding + changeset in the repository, and the effects of the patch and + changeset are visible in the working directory. You can undo + the application of a patch using the qpop command. MQ still + knows about, or manages, a popped patch, + but the patch no longer has a corresponding changeset in the + repository, and the working directory does not contain the + changes made by the patch. illustrates + the difference between applied and tracked patches. + +
    + Applied and unapplied patches in the MQ patch + stack + + + XXX add text + +
    + + You can reapply an unapplied, or popped, patch using the + qpush command. This + creates a new changeset to correspond to the patch, and the + patch's changes once again become present in the working + directory. See below for examples of qpop and qpush in action. +&interaction.mq.tutorial.qpop; + + Notice that once we have popped a patch or two patches, + the output of qseries + remains the same, while that of qapplied has changed. + + +
    + + Pushing and popping many patches + + While qpush and + qpop each operate on a + single patch at a time by default, you can push and pop many + patches in one go. The option to + qpush causes it to push + all unapplied patches, while the option to qpop causes it to pop all applied + patches. (For some more ways to push and pop many patches, + see below.) + +&interaction.mq.tutorial.qpush-a; + + + + Safety checks, and overriding them + + Several MQ commands check the working directory before + they do anything, and fail if they find any modifications. + They do this to ensure that you won't lose any changes that + you have made, but not yet incorporated into a patch. The + example below illustrates this; the qnew command will not create a + new patch if there are outstanding changes, caused in this + case by the hg add of + file3. + +&interaction.mq.tutorial.add; + + Commands that check the working directory all take an + I know what I'm doing option, which is always + named . The exact meaning of + depends on the command. For example, + hg qnew + will incorporate any outstanding changes into the new patch it + creates, but hg qpop + will revert modifications to any files affected by the patch + that it is popping. Be sure to read the documentation for a + command's option before you use it! + + + + Working on several patches at once + + The qrefresh command + always refreshes the topmost applied + patch. This means that you can suspend work on one patch (by + refreshing it), pop or push to make a different patch the top, + and work on that patch for a + while. + + Here's an example that illustrates how you can use this + ability. Let's say you're developing a new feature as two + patches. The first is a change to the core of your software, + and the second&emdash;layered on top of the + first&emdash;changes the user interface to use the code you + just added to the core. If you notice a bug in the core while + you're working on the UI patch, it's easy to fix the core. + Simply qrefresh the UI + patch to save your in-progress changes, and qpop down to the core patch. Fix + the core bug, qrefresh the + core patch, and qpush back + to the UI patch to continue where you left off. + + +
    + + More about patches + + MQ uses the GNU patch command to apply + patches, so it's helpful to know a few more detailed aspects of + how patch works, and about patches + themselves. + + + The strip count + + If you look at the file headers in a patch, you will + notice that the pathnames usually have an extra component on + the front that isn't present in the actual path name. This is + a holdover from the way that people used to generate patches + (people still do this, but it's somewhat rare with modern + revision control tools). + + Alice would unpack a tarball, edit her files, then decide + that she wanted to create a patch. So she'd rename her + working directory, unpack the tarball again (hence the need + for the rename), and use the and options to + diff to recursively generate a patch + between the unmodified directory and the modified one. The + result would be that the name of the unmodified directory + would be at the front of the left-hand path in every file + header, and the name of the modified directory would be at the + front of the right-hand path. + + Since someone receiving a patch from the Alices of the net + would be unlikely to have unmodified and modified directories + with exactly the same names, the patch + command has a option + that indicates the number of leading path name components to + strip when trying to apply a patch. This number is called the + strip count. + + An option of -p1 means + use a strip count of one. If + patch sees a file name + foo/bar/baz in a file header, it will + strip foo and try to patch a file named + bar/baz. (Strictly speaking, the strip + count refers to the number of path + separators (and the components that go with them + ) to strip. A strip count of one will turn + foo/bar into bar, + but /foo/bar (notice the extra leading + slash) into foo/bar.) + + The standard strip count for patches is + one; almost all patches contain one leading path name + component that needs to be stripped. Mercurial's hg diff command generates path names + in this form, and the hg + import command and MQ expect patches to have a + strip count of one. + + If you receive a patch from someone that you want to add + to your patch queue, and the patch needs a strip count other + than one, you cannot just qimport the patch, because + qimport does not yet have + a -p option (see issue + 311). Your best bet is to qnew a patch of your own, then + use patch -pN to apply their patch, + followed by hg addremove to + pick up any files added or removed by the patch, followed by + hg qrefresh. This + complexity may become unnecessary; see issue + 311 for details. + + + + Strategies for applying a patch + + When patch applies a hunk, it tries a + handful of successively less accurate strategies to try to + make the hunk apply. This falling-back technique often makes + it possible to take a patch that was generated against an old + version of a file, and apply it against a newer version of + that file. + + First, patch tries an exact match, + where the line numbers, the context, and the text to be + modified must apply exactly. If it cannot make an exact + match, it tries to find an exact match for the context, + without honouring the line numbering information. If this + succeeds, it prints a line of output saying that the hunk was + applied, but at some offset from the + original line number. + + If a context-only match fails, patch + removes the first and last lines of the context, and tries a + reduced context-only match. If the hunk + with reduced context succeeds, it prints a message saying that + it applied the hunk with a fuzz factor + (the number after the fuzz factor indicates how many lines of + context patch had to trim before the patch + applied). + + When neither of these techniques works, + patch prints a message saying that the hunk + in question was rejected. It saves rejected hunks (also + simply called rejects) to a file with the same + name, and an added .rej + extension. It also saves an unmodified copy of the file with + a .orig extension; the + copy of the file without any extensions will contain any + changes made by hunks that did apply + cleanly. If you have a patch that modifies + foo with six hunks, and one of them fails + to apply, you will have: an unmodified + foo.orig, a foo.rej + containing one hunk, and foo, containing + the changes made by the five successful hunks. + + + + Some quirks of patch representation + + There are a few useful things to know about how + patch works with files. + + This should already be obvious, but + patch cannot handle binary + files. + + Neither does it care about the executable bit; + it creates new files as readable, but not + executable. + + patch treats the removal of + a file as a diff between the file to be removed and the + empty file. So your idea of I deleted this + file looks like every line of this file + was deleted in a patch. + + It treats the addition of a file as a diff + between the empty file and the file to be added. So in a + patch, your idea of I added this file looks + like every line of this file was + added. + + It treats a renamed file as the removal of the + old name, and the addition of the new name. This means + that renamed files have a big footprint in patches. (Note + also that Mercurial does not currently try to infer when + files have been renamed or copied in a patch.) + + patch cannot represent + empty files, so you cannot use a patch to represent the + notion I added this empty file to the + tree. + + + + Beware the fuzz + + While applying a hunk at an offset, or with a fuzz factor, + will often be completely successful, these inexact techniques + naturally leave open the possibility of corrupting the patched + file. The most common cases typically involve applying a + patch twice, or at an incorrect location in the file. If + patch or qpush ever mentions an offset or + fuzz factor, you should make sure that the modified files are + correct afterwards. + + It's often a good idea to refresh a patch that has applied + with an offset or fuzz factor; refreshing the patch generates + new context information that will make it apply cleanly. I + say often, not always, because + sometimes refreshing a patch will make it fail to apply + against a different revision of the underlying files. In some + cases, such as when you're maintaining a patch that must sit + on top of multiple versions of a source tree, it's acceptable + to have a patch apply with some fuzz, provided you've verified + the results of the patching process in such cases. + + + + Handling rejection + + If qpush fails to + apply a patch, it will print an error message and exit. If it + has left .rej files + behind, it is usually best to fix up the rejected hunks before + you push more patches or do any further work. + + If your patch used to apply cleanly, + and no longer does because you've changed the underlying code + that your patches are based on, Mercurial Queues can help; see + for details. + + Unfortunately, there aren't any great techniques for + dealing with rejected hunks. Most often, you'll need to view + the .rej file and edit the + target file, applying the rejected hunks by hand. + + If you're feeling adventurous, Neil Brown, a Linux kernel + hacker, wrote a tool called wiggle + web:wiggle, which is more vigorous than + patch in its attempts to make a patch + apply. + + Another Linux kernel hacker, Chris Mason (the author of + Mercurial Queues), wrote a similar tool called + mpatch web:mpatch, + which takes a simple approach to automating the application of + hunks rejected by patch. The + mpatch command can help with four common + reasons that a hunk may be rejected: + + + The context in the middle of a hunk has + changed. + + A hunk is missing some context at the + beginning or end. + + A large hunk might apply better&emdash;either + entirely or in part&emdash;if it was broken up into + smaller hunks. + + A hunk removes lines with slightly different + content than those currently present in the file. + + + If you use wiggle or + mpatch, you should be doubly careful to + check your results when you're done. In fact, + mpatch enforces this method of + double-checking the tool's output, by automatically dropping + you into a merge program when it has done its job, so that you + can verify its work and finish off any remaining + merges. + + + + + Getting the best performance out of MQ + + MQ is very efficient at handling a large number of patches. + I ran some performance experiments in mid-2006 for a talk that I + gave at the 2006 EuroPython conference + web:europython. I used as my data set the + Linux 2.6.17-mm1 patch series, which consists of 1,738 patches. + I applied these on top of a Linux kernel repository containing + all 27,472 revisions between Linux 2.6.12-rc2 and Linux + 2.6.17. + + On my old, slow laptop, I was able to hg qpush all + 1,738 patches in 3.5 minutes, and hg qpop + + them all in 30 seconds. (On a newer laptop, the time to push + all patches dropped to two minutes.) I could qrefresh one of the biggest patches + (which made 22,779 lines of changes to 287 files) in 6.6 + seconds. + + Clearly, MQ is well suited to working in large trees, but + there are a few tricks you can use to get the best performance + of it. + + First of all, try to batch operations + together. Every time you run qpush or qpop, these commands scan the + working directory once to make sure you haven't made some + changes and then forgotten to run qrefresh. On a small tree, the + time that this scan takes is unnoticeable. However, on a + medium-sized tree (containing tens of thousands of files), it + can take a second or more. + + The qpush and qpop commands allow you to push and + pop multiple patches at a time. You can identify the + destination patch that you want to end up at. + When you qpush with a + destination specified, it will push patches until that patch is + at the top of the applied stack. When you qpop to a destination, MQ will pop + patches until the destination patch is at the top. + + You can identify a destination patch using either the name + of the patch, or by number. If you use numeric addressing, + patches are counted from zero; this means that the first patch + is zero, the second is one, and so on. + + + + Updating your patches when the underlying code + changes + + It's common to have a stack of patches on top of an + underlying repository that you don't modify directly. If you're + working on changes to third-party code, or on a feature that is + taking longer to develop than the rate of change of the code + beneath, you will often need to sync up with the underlying + code, and fix up any hunks in your patches that no longer apply. + This is called rebasing your patch + series. + + The simplest way to do this is to hg + qpop your patches, then hg pull changes into the underlying + repository, and finally hg qpush your + patches again. MQ will stop pushing any time it runs across a + patch that fails to apply during conflicts, allowing you to fix + your conflicts, qrefresh the + affected patch, and continue pushing until you have fixed your + entire stack. + + This approach is easy to use and works well if you don't + expect changes to the underlying code to affect how well your + patches apply. If your patch stack touches code that is modified + frequently or invasively in the underlying repository, however, + fixing up rejected hunks by hand quickly becomes + tiresome. + + It's possible to partially automate the rebasing process. + If your patches apply cleanly against some revision of the + underlying repo, MQ can use this information to help you to + resolve conflicts between your patches and a different + revision. + + The process is a little involved. + + To begin, hg qpush + -a all of your patches on top of the revision + where you know that they apply cleanly. + + Save a backup copy of your patch directory using + hg qsave . + This prints the name of the directory that it has saved the + patches in. It will save the patches to a directory called + .hg/patches.N, where + N is a small integer. It also commits a + save changeset on top of your applied + patches; this is for internal book-keeping, and records the + states of the series and + status files. + + Use hg pull to + bring new changes into the underlying repository. (Don't + run hg pull -u; see below + for why.) + + Update to the new tip revision, using hg update to override + the patches you have pushed. + + Merge all patches using hg qpush -m + -a. The option to + qpush tells MQ to + perform a three-way merge if the patch fails to + apply. + + + During the hg qpush , + each patch in the series + file is applied normally. If a patch applies with fuzz or + rejects, MQ looks at the queue you qsaved, and performs a three-way + merge with the corresponding changeset. This merge uses + Mercurial's normal merge machinery, so it may pop up a GUI merge + tool to help you to resolve problems. + + When you finish resolving the effects of a patch, MQ + refreshes your patch based on the result of the merge. + + At the end of this process, your repository will have one + extra head from the old patch queue, and a copy of the old patch + queue will be in .hg/patches.N. You can remove the + extra head using hg qpop -a -n + patches.N or hg + strip. You can delete .hg/patches.N once you are sure + that you no longer need it as a backup. + + + + Identifying patches + + MQ commands that work with patches let you refer to a patch + either by using its name or by a number. By name is obvious + enough; pass the name foo.patch to qpush, for example, and it will + push patches until foo.patch is + applied. + + As a shortcut, you can refer to a patch using both a name + and a numeric offset; foo.patch-2 means + two patches before foo.patch, + while bar.patch+4 means four patches + after bar.patch. + + Referring to a patch by index isn't much different. The + first patch printed in the output of qseries is patch zero (yes, it's + one of those start-at-zero counting systems); the second is + patch one; and so on. + + MQ also makes it easy to work with patches when you are + using normal Mercurial commands. Every command that accepts a + changeset ID will also accept the name of an applied patch. MQ + augments the tags normally in the repository with an eponymous + one for each applied patch. In addition, the special tags + qbase and + qtip identify + the bottom-most and topmost applied patches, + respectively. + + These additions to Mercurial's normal tagging capabilities + make dealing with patches even more of a breeze. + + Want to patchbomb a mailing list with your + latest series of changes? + hg email qbase:qtip + (Don't know what patchbombing is? See + .) + + Need to see all of the patches since + foo.patch that have touched files in a + subdirectory of your tree? + hg log -r foo.patch:qtip subdir + + + + Because MQ makes the names of patches available to the rest + of Mercurial through its normal internal tag machinery, you + don't need to type in the entire name of a patch when you want + to identify it by name. + + Another nice consequence of representing patch names as tags + is that when you run the hg log + command, it will display a patch's name as a tag, simply as part + of its normal output. This makes it easy to visually + distinguish applied patches from underlying + normal revisions. The following example shows a + few normal Mercurial commands in use with applied + patches. + +&interaction.mq.id.output; + + + + Useful things to know about + + There are a number of aspects of MQ usage that don't fit + tidily into sections of their own, but that are good to know. + Here they are, in one place. + + + Normally, when you qpop a patch and qpush it again, the changeset + that represents the patch after the pop/push will have a + different identity than the changeset + that represented the hash beforehand. See for + information as to why this is. + + It's not a good idea to hg merge changes from another + branch with a patch changeset, at least if you want to + maintain the patchiness of that changeset and + changesets below it on the patch stack. If you try to do + this, it will appear to succeed, but MQ will become + confused. + + + + + Managing patches in a repository + + Because MQ's .hg/patches directory resides + outside a Mercurial repository's working directory, the + underlying Mercurial repository knows nothing + about the management or presence of patches. + + This presents the interesting possibility of managing the + contents of the patch directory as a Mercurial repository in its + own right. This can be a useful way to work. For example, you + can work on a patch for a while, qrefresh it, then hg commit the current state of the + patch. This lets you roll back to that version + of the patch later on. + + You can then share different versions of the same patch + stack among multiple underlying repositories. I use this when I + am developing a Linux kernel feature. I have a pristine copy of + my kernel sources for each of several CPU architectures, and a + cloned repository under each that contains the patches I am + working on. When I want to test a change on a different + architecture, I push my current patches to the patch repository + associated with that kernel tree, pop and push all of my + patches, and build and test that kernel. + + Managing patches in a repository makes it possible for + multiple developers to work on the same patch series without + colliding with each other, all on top of an underlying source + base that they may or may not control. + + + MQ support for patch repositories + + MQ helps you to work with the .hg/patches directory as a + repository; when you prepare a repository for working with + patches using qinit, you + can pass the option to create the .hg/patches directory as a + Mercurial repository. + + + If you forget to use the option, you + can simply go into the .hg/patches directory at any + time and run hg init. + Don't forget to add an entry for the status file to the .hgignore file, though + + (hg qinit + does this for you automatically); you + really don't want to manage the + status file. + + + As a convenience, if MQ notices that the .hg/patches directory is a + repository, it will automatically hg + add every patch that you create and import. + + MQ provides a shortcut command, qcommit, that runs hg commit in the .hg/patches + directory. This saves some bothersome typing. + + Finally, as a convenience to manage the patch directory, + you can define the alias mq on Unix + systems. For example, on Linux systems using the + bash shell, you can include the following + snippet in your ~/.bashrc. + + alias mq=`hg -R $(hg root)/.hg/patches' + + You can then issue commands of the form mq + pull from the main repository. + + + + A few things to watch out for + + MQ's support for working with a repository full of patches + is limited in a few small respects. + + MQ cannot automatically detect changes that you make to + the patch directory. If you hg + pull, manually edit, or hg + update changes to patches or the series file, you will have to + hg qpop and + then hg qpush in + the underlying repository to see those changes show up there. + If you forget to do this, you can confuse MQ's idea of which + patches are applied. + + + + + Third party tools for working with patches + + Once you've been working with patches for a while, you'll + find yourself hungry for tools that will help you to understand + and manipulate the patches you're dealing with. + + The diffstat command + web:diffstat generates a histogram of the + modifications made to each file in a patch. It provides a good + way to get a sense of a patch&emdash;which files + it affects, and how much change it introduces to each file and + as a whole. (I find that it's a good idea to use + diffstat's option as a matter of + course, as otherwise it will try to do clever things with + prefixes of file names that inevitably confuse at least + me.) + +&interaction.mq.tools.tools; + + The patchutils package + web:patchutils is invaluable. It provides a + set of small utilities that follow the Unix + philosophy; each does one useful thing with a patch. + The patchutils command I use + most is filterdiff, which extracts subsets + from a patch file. For example, given a patch that modifies + hundreds of files across dozens of directories, a single + invocation of filterdiff can generate a + smaller patch that only touches files whose names match a + particular glob pattern. See for another + example. + + + + Good ways to work with patches + + Whether you are working on a patch series to submit to a + free software or open source project, or a series that you + intend to treat as a sequence of regular changesets when you're + done, you can use some simple techniques to keep your work well + organized. + + Give your patches descriptive names. A good name for a + patch might be rework-device-alloc.patch, + because it will immediately give you a hint what the purpose of + the patch is. Long names shouldn't be a problem; you won't be + typing the names often, but you will be + running commands like qapplied and qtop over and over. Good naming + becomes especially important when you have a number of patches + to work with, or if you are juggling a number of different tasks + and your patches only get a fraction of your attention. + + Be aware of what patch you're working on. Use the qtop command and skim over the text + of your patches frequently&emdash;for example, using hg tip )&emdash;to be sure + of where you stand. I have several times worked on and qrefreshed a patch other than the + one I intended, and it's often tricky to migrate changes into + the right patch after making them in the wrong one. + + For this reason, it is very much worth investing a little + time to learn how to use some of the third-party tools I + described in , + particularly + diffstat and filterdiff. + The former will give you a quick idea of what changes your patch + is making, while the latter makes it easy to splice hunks + selectively out of one patch and into another. + + + + MQ cookbook + + + Manage <quote>trivial</quote> patches + + Because the overhead of dropping files into a new + Mercurial repository is so low, it makes a lot of sense to + manage patches this way even if you simply want to make a few + changes to a source tarball that you downloaded. + + Begin by downloading and unpacking the source tarball, and + turning it into a Mercurial repository. + + &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 hg + locate above deletes all files in the working + directory, so that hg + commit's option can + actually tell which files have really been removed in the + newer version of the source. + + Finally, you can apply your patches on top of the new + tree. + + &interaction.mq.tarball.repush; + + + + Combining entire patches + + MQ provides a command, qfold that lets you combine + entire patches. This folds the patches you + name, in the order you name them, into the topmost applied + patch, and concatenates their descriptions onto the end of its + description. The patches that you fold must be unapplied + before you fold them. + + The order in which you fold patches matters. If your + topmost applied patch is foo, and you + qfold + bar and quux into it, + you will end up with a patch that has the same effect as if + you applied first foo, then + bar, followed by + quux. + + + + Merging part of one patch into another + + Merging part of one patch into + another is more difficult than combining entire + patches. + + If you want to move changes to entire files, you can use + filterdiff's and options to choose the + modifications to snip out of one patch, concatenating its + output onto the end of the patch you want to merge into. You + usually won't need to modify the patch you've merged the + changes from. Instead, MQ will report some rejected hunks + when you qpush it (from + the hunks you moved into the other patch), and you can simply + qrefresh the patch to drop + the duplicate hunks. + + If you have a patch that has multiple hunks modifying a + file, and you only want to move a few of those hunks, the job + becomes more messy, but you can still partly automate it. Use + lsdiff -nvv to print some metadata about + the patch. + + &interaction.mq.tools.lsdiff; + + This command prints three different kinds of + number: + + (in the first column) a file + number to identify each file modified in the + patch; + + (on the next line, indented) the line number + within a modified file where a hunk starts; and + + (on the same line) a hunk + number to identify that hunk. + + + You'll have to use some visual inspection, and reading of + the patch, to identify the file and hunk numbers you'll want, + but you can then pass them to to + filterdiff's and options, to + select exactly the file and hunk you want to extract. + + Once you have this hunk, you can concatenate it onto the + end of your destination patch and continue with the remainder + of . + + + + + Differences between quilt and MQ + + If you are already familiar with quilt, MQ provides a + similar command set. There are a few differences in the way + that it works. + + You will already have noticed that most quilt commands have + MQ counterparts that simply begin with a + q. The exceptions are quilt's + add and remove commands, + the counterparts for which are the normal Mercurial hg add and hg + remove commands. There is no MQ equivalent of the + quilt edit command. + + +
    + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch12-mq-collab.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch12-mq-collab.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,518 @@ + + + + + Advanced uses of Mercurial Queues + + While it's easy to pick up straightforward uses of Mercurial + Queues, use of a little discipline and some of MQ's less + frequently used capabilities makes it possible to work in + complicated development environments. + + In this chapter, I will use as an example a technique I have + used to manage the development of an Infiniband device driver for + the Linux kernel. The driver in question is large (at least as + drivers go), with 25,000 lines of code spread across 35 source + files. It is maintained by a small team of developers. + + While much of the material in this chapter is specific to + Linux, the same principles apply to any code base for which you're + not the primary owner, and upon which you need to do a lot of + development. + + + The problem of many targets + + The Linux kernel changes rapidly, and has never been + internally stable; developers frequently make drastic changes + between releases. This means that a version of the driver that + works well with a particular released version of the kernel will + not even compile correctly against, + typically, any other version. + + To maintain a driver, we have to keep a number of distinct + versions of Linux in mind. + + One target is the main Linux kernel development + tree. Maintenance of the code is in this case partly shared + by other developers in the kernel community, who make + drive-by modifications to the driver as they + develop and refine kernel subsystems. + + We also maintain a number of + backports to older versions of the Linux + kernel, to support the needs of customers who are running + older Linux distributions that do not incorporate our + drivers. (To backport a piece of code + is to modify it to work in an older version of its target + environment than the version it was developed for.) + + Finally, we make software releases on a schedule + that is necessarily not aligned with those used by Linux + distributors and kernel developers, so that we can deliver + new features to customers without forcing them to upgrade + their entire kernels or distributions. + + + + Tempting approaches that don't work well + + There are two standard ways to maintain a + piece of software that has to target many different + environments. + + The first is to maintain a number of branches, each + intended for a single target. The trouble with this approach + is that you must maintain iron discipline in the flow of + changes between repositories. A new feature or bug fix must + start life in a pristine repository, then + percolate out to every backport repository. Backport changes + are more limited in the branches they should propagate to; a + backport change that is applied to a branch where it doesn't + belong will probably stop the driver from compiling. + + The second is to maintain a single source tree filled with + conditional statements that turn chunks of code on or off + depending on the intended target. Because these + ifdefs are not allowed in the Linux kernel + tree, a manual or automatic process must be followed to strip + them out and yield a clean tree. A code base maintained in + this fashion rapidly becomes a rat's nest of conditional + blocks that are difficult to understand and maintain. + + Neither of these approaches is well suited to a situation + where you don't own the canonical copy of a + source tree. In the case of a Linux driver that is + distributed with the standard kernel, Linus's tree contains + the copy of the code that will be treated by the world as + canonical. The upstream version of my driver + can be modified by people I don't know, without me even + finding out about it until after the changes show up in + Linus's tree. + + These approaches have the added weakness of making it + difficult to generate well-formed patches to submit + upstream. + + In principle, Mercurial Queues seems like a good candidate + to manage a development scenario such as the above. While + this is indeed the case, MQ contains a few added features that + make the job more pleasant. + + + + + Conditionally applying patches with guards + + Perhaps the best way to maintain sanity with so many targets + is to be able to choose specific patches to apply for a given + situation. MQ provides a feature called guards + (which originates with quilt's guards + command) that does just this. To start off, let's create a + simple repository for experimenting in. + + &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 guard, + which is simply a text string of your choosing, then tell MQ to + select specific guards to use when applying patches. MQ will + then either apply, or skip over, a guarded patch, depending on + the guards that you have selected. + + A patch can have an arbitrary number of guards; each one is + positive (apply this patch if this + guard is selected) or negative + (skip this patch if this guard is selected). A + patch with no guards is always applied. + + + + Controlling the guards on a patch + + The qguard command lets + you determine which guards should apply to a patch, or display + the guards that are already in effect. Without any arguments, it + displays the guards on the current topmost patch. + + &interaction.mq.guards.qguard; + + To set a positive guard on a patch, prefix the name of the + guard with a +. + + &interaction.mq.guards.qguard.pos; + + To set a negative guard + on a patch, prefix the name of the guard with a + -. + + &interaction.mq.guards.qguard.neg; + + + The qguard command + sets the guards on a patch; it doesn't + modify them. What this means is that if + you run hg qguard +a +b on a + patch, then hg qguard +c on + the same patch, the only guard that will + be set on it afterwards is +c. + + + Mercurial stores guards in the series file; the form in which they + are stored is easy both to understand and to edit by hand. (In + other words, you don't have to use the qguard command if you don't want + to; it's okay to simply edit the series file.) + + &interaction.mq.guards.series; + + + + Selecting the guards to use + + The qselect command + determines which guards are active at a given time. The effect + of this is to determine which patches MQ will apply the next + time you run qpush. It has + no other effect; in particular, it doesn't do anything to + patches that are already applied. + + With no arguments, the qselect command lists the guards + currently in effect, one per line of output. Each argument is + treated as the name of a guard to apply. + + &interaction.mq.guards.qselect.foo; + + In case you're interested, the currently selected guards are + stored in the guards file. + + &interaction.mq.guards.qselect.cat; + + We can see the effect the selected guards have when we run + qpush. + + &interaction.mq.guards.qselect.qpush; + + A guard cannot start with a + + or + - character. The name of a + guard must not contain white space, but most other characters + are acceptable. If you try to use a guard with an invalid name, + MQ will complain: + + &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; + + + + MQ's rules for applying patches + + The rules that MQ uses when deciding whether to apply a + patch are as follows. + + A patch that has no guards is always + applied. + + If the patch has any negative guard that matches + any currently selected guard, the patch is skipped. + + If the patch has any positive guard that matches + any currently selected guard, the patch is applied. + + If the patch has positive or negative guards, + but none matches any currently selected guard, the patch is + skipped. + + + + + Trimming the work environment + + In working on the device driver I mentioned earlier, I don't + apply the patches to a normal Linux kernel tree. Instead, I use + a repository that contains only a snapshot of the source files + and headers that are relevant to Infiniband development. This + repository is 1% the size of a kernel repository, so it's easier + to work with. + + I then choose a base version on top of which + the patches are applied. This is a snapshot of the Linux kernel + tree as of a revision of my choosing. When I take the snapshot, + I record the changeset ID from the kernel repository in the + commit message. Since the snapshot preserves the + shape and content of the relevant parts of the + kernel tree, I can apply my patches on top of either my tiny + repository or a normal kernel tree. + + Normally, the base tree atop which the patches apply should + be a snapshot of a very recent upstream tree. This best + facilitates the development of patches that can easily be + submitted upstream with few or no modifications. + + + + Dividing up the <filename role="special">series</filename> + file + + I categorise the patches in the series file into a number of logical + groups. Each section of like patches begins with a block of + comments that describes the purpose of the patches that + follow. + + The sequence of patch groups that I maintain follows. The + ordering of these groups is important; I'll describe why after I + introduce the groups. + + The accepted group. Patches that + the development team has submitted to the maintainer of the + Infiniband subsystem, and which he has accepted, but which + are not present in the snapshot that the tiny repository is + based on. These are read only patches, + present only to transform the tree into a similar state as + it is in the upstream maintainer's repository. + + The rework group. Patches that I + have submitted, but that the upstream maintainer has + requested modifications to before he will accept + them. + + The pending group. Patches that + I have not yet submitted to the upstream maintainer, but + which we have finished working on. These will be read + only for a while. If the upstream maintainer + accepts them upon submission, I'll move them to the end of + the accepted group. If he requests that I + modify any, I'll move them to the beginning of the + rework group. + + The in progress group. Patches + that are actively being developed, and should not be + submitted anywhere yet. + + The backport group. Patches that + adapt the source tree to older versions of the kernel + tree. + + The do not ship group. Patches + that for some reason should never be submitted upstream. + For example, one such patch might change embedded driver + identification strings to make it easier to distinguish, in + the field, between an out-of-tree version of the driver and + a version shipped by a distribution vendor. + + + Now to return to the reasons for ordering groups of patches + in this way. We would like the lowest patches in the stack to + be as stable as possible, so that we will not need to rework + higher patches due to changes in context. Putting patches that + will never be changed first in the series file serves this + purpose. + + We would also like the patches that we know we'll need to + modify to be applied on top of a source tree that resembles the + upstream tree as closely as possible. This is why we keep + accepted patches around for a while. + + The backport and do not ship + patches float at the end of the series file. The backport patches + must be applied on top of all other patches, and the do + not ship patches might as well stay out of harm's + way. + + + + Maintaining the patch series + + In my work, I use a number of guards to control which + patches are to be applied. + + + Accepted patches are guarded with + accepted. I enable this guard most of + the time. When I'm applying the patches on top of a tree + where the patches are already present, I can turn this patch + off, and the patches that follow it will apply + cleanly. + + Patches that are finished, but + not yet submitted, have no guards. If I'm applying the + patch stack to a copy of the upstream tree, I don't need to + enable any guards in order to get a reasonably safe source + tree. + + Those patches that need reworking before being + resubmitted are guarded with + rework. + + For those patches that are still under + development, I use devel. + + A backport patch may have several guards, one + for each version of the kernel to which it applies. For + example, a patch that backports a piece of code to 2.6.9 + will have a 2.6.9 guard. + + This variety of guards gives me considerable flexibility in + determining what kind of source tree I want to end up with. For + most situations, the selection of appropriate guards is + automated during the build process, but I can manually tune the + guards to use for less common circumstances. + + + The art of writing backport patches + + Using MQ, writing a backport patch is a simple process. + All such a patch has to do is modify a piece of code that uses + a kernel feature not present in the older version of the + kernel, so that the driver continues to work correctly under + that older version. + + A useful goal when writing a good backport patch is to + make your code look as if it was written for the older version + of the kernel you're targeting. The less obtrusive the patch, + the easier it will be to understand and maintain. If you're + writing a collection of backport patches to avoid the + rat's nest effect of lots of + #ifdefs (hunks of source code that are only + used conditionally) in your code, don't introduce + version-dependent #ifdefs into the patches. + Instead, write several patches, each of which makes + unconditional changes, and control their application using + guards. + + There are two reasons to divide backport patches into a + distinct group, away from the regular patches + whose effects they modify. The first is that intermingling the + two makes it more difficult to use a tool like the patchbomb extension to automate the + process of submitting the patches to an upstream maintainer. + The second is that a backport patch could perturb the context + in which a subsequent regular patch is applied, making it + impossible to apply the regular patch cleanly + without the earlier backport patch + already being applied. + + + + + Useful tips for developing with MQ + + + Organising patches in directories + + If you're working on a substantial project with MQ, it's + not difficult to accumulate a large number of patches. For + example, I have one patch repository that contains over 250 + patches. + + If you can group these patches into separate logical + categories, you can if you like store them in different + directories; MQ has no problems with patch names that contain + path separators. + + + + Viewing the history of a patch + + 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 . If you do + so, you'll quickly + discover that using the hg + diff command to look at the history of changes to + a patch is unworkable. This is in part because you're looking + at the second derivative of the real code (a diff of a diff), + but also because MQ adds noise to the process by modifying + time stamps and directory names when it updates a + patch. + + However, you can use the extdiff extension, which is bundled + with Mercurial, to turn a diff of two versions of a patch into + something readable. To do this, you will need a third-party + package called patchutils + web:patchutils. This provides a command + named interdiff, which shows the + differences between two diffs as a diff. Used on two versions + of the same diff, it generates a diff that represents the diff + from the first to the second version. + + You can enable the extdiff extension in the usual way, + by adding a line to the extensions section of your + ~/.hgrc. + [extensions] +extdiff = + The interdiff command expects to be + passed the names of two files, but the extdiff extension passes the program + it runs a pair of directories, each of which can contain an + arbitrary number of files. We thus need a small program that + will run interdiff on each pair of files in + these two directories. This program is available as hg-interdiff in the examples directory of the + source code repository that accompanies this book. + + With the hg-interdiff + program in your shell's search path, you can run it as + follows, from inside an MQ patch directory: + hg extdiff -p hg-interdiff -r A:B my-change.patch + Since you'll probably want to use this long-winded command + a lot, you can get hgext to + make it available as a normal Mercurial command, again by + editing your ~/.hgrc. + [extdiff] +cmd.interdiff = hg-interdiff + This directs hgext to + make an interdiff command available, so you + can now shorten the previous invocation of extdiff to something a + little more wieldy. + hg interdiff -r A:B my-change.patch + + + The interdiff command works well + only if the underlying files against which versions of a + patch are generated remain the same. If you create a patch, + modify the underlying files, and then regenerate the patch, + interdiff may not produce useful + output. + + + The extdiff extension is + useful for more than merely improving the presentation of MQ + patches. To read more about it, go to . + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/ch13-hgext.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch13-hgext.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,554 @@ + + + + + Adding functionality with extensions + + While the core of Mercurial is quite complete from a + functionality standpoint, it's deliberately shorn of fancy + features. This approach of preserving simplicity keeps the + software easy to deal with for both maintainers and users. + + However, Mercurial doesn't box you in with an inflexible + command set: you can add features to it as + extensions (sometimes known as + plugins). We've already discussed a few of + these extensions in earlier chapters. + + + covers the fetch extension; + this combines pulling new changes and merging them with local + changes into a single command, fetch. + + In , we covered + several extensions that are useful for hook-related + functionality: acl adds + access control lists; bugzilla adds integration with the + Bugzilla bug tracking system; and notify sends notification emails on + new changes. + + The Mercurial Queues patch management extension is + so invaluable that it merits two chapters and an appendix all + to itself. covers the + basics; discusses advanced topics; + and goes into detail on + each + command. + + + In this chapter, we'll cover some of the other extensions that + are available for Mercurial, and briefly touch on some of the + machinery you'll need to know about if you want to write an + extension of your own. + + In , + we'll discuss the possibility of huge + performance improvements using the inotify extension. + + + + Improve performance with the <literal + role="hg-ext">inotify</literal> extension + + Are you interested in having some of the most common + Mercurial operations run as much as a hundred times faster? + Read on! + + Mercurial has great performance under normal circumstances. + For example, when you run the hg + status command, Mercurial has to scan almost every + directory and file in your repository so that it can display + file status. Many other Mercurial commands need to do the same + work behind the scenes; for example, the hg diff command uses the status + machinery to avoid doing an expensive comparison operation on + files that obviously haven't changed. + + Because obtaining file status is crucial to good + performance, the authors of Mercurial have optimised this code + to within an inch of its life. However, there's no avoiding the + fact that when you run hg + status, Mercurial is going to have to perform at + least one expensive system call for each managed file to + determine whether it's changed since the last time Mercurial + checked. For a sufficiently large repository, this can take a + long time. + + To put a number on the magnitude of this effect, I created a + repository containing 150,000 managed files. I timed hg status as taking ten seconds to + run, even when none of those files had been + modified. + + Many modern operating systems contain a file notification + facility. If a program signs up to an appropriate service, the + operating system will notify it every time a file of interest is + created, modified, or deleted. On Linux systems, the kernel + component that does this is called + inotify. + + Mercurial's inotify + extension talks to the kernel's inotify + component to optimise hg status + commands. The extension has two components. A daemon sits in + the background and receives notifications from the + inotify subsystem. It also listens for + connections from a regular Mercurial command. The extension + modifies Mercurial's behavior so that instead of scanning the + filesystem, it queries the daemon. Since the daemon has perfect + information about the state of the repository, it can respond + with a result instantaneously, avoiding the need to scan every + directory and file in the repository. + + Recall the ten seconds that I measured plain Mercurial as + taking to run hg status on a + 150,000 file repository. With the inotify extension enabled, the time + dropped to 0.1 seconds, a factor of one + hundred faster. + + Before we continue, please pay attention to some + caveats. + + The inotify + extension is Linux-specific. Because it interfaces directly + to the Linux kernel's inotify subsystem, + it does not work on other operating systems. + + It should work on any Linux distribution that + was released after early 2005. Older distributions are + likely to have a kernel that lacks + inotify, or a version of + glibc that does not have the necessary + interfacing support. + + Not all filesystems are suitable for use with + the inotify extension. + Network filesystems such as NFS are a non-starter, for + example, particularly if you're running Mercurial on several + systems, all mounting the same network filesystem. The + kernel's inotify system has no way of + knowing about changes made on another system. Most local + filesystems (e.g. ext3, XFS, ReiserFS) should work + fine. + + + The inotify extension is + not yet shipped with Mercurial as of May 2007, so it's a little + more involved to set up than other extensions. But the + performance improvement is worth it! + + The extension currently comes in two parts: a set of patches + to the Mercurial source code, and a library of Python bindings + to the inotify subsystem. + + There are two Python + inotify binding libraries. One of them is + called pyinotify, and is packaged by some + Linux distributions as python-inotify. + This is not the one you'll need, as it is + too buggy and inefficient to be practical. + + To get going, it's best to already have a functioning copy + of Mercurial installed. + + If you follow the instructions below, you'll be + replacing and overwriting any existing + installation of Mercurial that you might already have, using + the latest bleeding edge Mercurial code. Don't + say you weren't warned! + + + Clone the Python inotify + binding repository. Build and install it. + hg clone http://hg.kublai.com/python/inotify +cd inotify +python setup.py build --force +sudo python setup.py install --skip-build + + Clone the crew Mercurial repository. + Clone the inotify patch + repository so that Mercurial Queues will be able to apply + patches to your cope of the crew repository. + hg clone http://hg.intevation.org/mercurial/crew +hg clone crew inotify +hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches + + Make sure that you have the Mercurial Queues + extension, mq, enabled. If + you've never used MQ, read to get started + quickly. + + Go into the inotify repo, and apply all + of the inotify patches + using the option to the qpush command. + cd inotify +hg qpush -a + + If you get an error message from qpush, you should not continue. + Instead, ask for help. + + Build and install the patched version of + Mercurial. + python setup.py build --force +sudo python setup.py install --skip-build + + + Once you've build a suitably patched version of Mercurial, + all you need to do to enable the inotify extension is add an entry to + your ~/.hgrc. + [extensions] inotify = + When the inotify extension + is enabled, Mercurial will automatically and transparently start + the status daemon the first time you run a command that needs + status in a repository. It runs one status daemon per + repository. + + The status daemon is started silently, and runs in the + background. If you look at a list of running processes after + you've enabled the inotify + extension and run a few commands in different repositories, + you'll thus see a few hg processes sitting + around, waiting for updates from the kernel and queries from + Mercurial. + + The first time you run a Mercurial command in a repository + when you have the inotify + extension enabled, it will run with about the same performance + as a normal Mercurial command. This is because the status + daemon needs to perform a normal status scan so that it has a + baseline against which to apply later updates from the kernel. + However, every subsequent command that does + any kind of status check should be noticeably faster on + repositories of even fairly modest size. Better yet, the bigger + your repository is, the greater a performance advantage you'll + see. The inotify daemon makes + status operations almost instantaneous on repositories of all + sizes! + + If you like, you can manually start a status daemon using + the inserve command. + This gives you slightly finer control over how the daemon ought + to run. This command will of course only be available when the + inotify extension is + enabled. + + When you're using the inotify extension, you should notice + no difference at all in Mercurial's + behavior, with the sole exception of status-related commands + running a whole lot faster than they used to. You should + specifically expect that commands will not print different + output; neither should they give different results. If either of + these situations occurs, please report a bug. + + + + Flexible diff support with the <literal + role="hg-ext">extdiff</literal> extension + + Mercurial's built-in hg + 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 extdiff extension. This will let you + use, for example, a graphical diff tool. + + The extdiff extension is + bundled with Mercurial, so it's easy to set up. In the extensions section of your + ~/.hgrc, simply add a + one-line entry to enable the extension. + [extensions] +extdiff = + This introduces a command named extdiff, which by default uses + your system's diff command to generate a + unified diff in the same form as the built-in hg diff command. + + &interaction.extdiff.extdiff; + + The result won't be exactly the same as with the built-in + hg diff variations, because the + output of diff varies from one system to + another, even when passed the same options. + + As the making snapshot + lines of output above imply, the extdiff command works by + creating two snapshots of your source tree. The first snapshot + is of the source revision; the second, of the target revision or + working directory. The extdiff command generates + these snapshots in a temporary directory, passes the name of + each directory to an external diff viewer, then deletes the + temporary directory. For efficiency, it only snapshots the + directories and files that have changed between the two + revisions. + + Snapshot directory names have the same base name as your + repository. If your repository path is /quux/bar/foo, then foo will be the name of each + snapshot directory. Each snapshot directory name has its + changeset ID appended, if appropriate. If a snapshot is of + revision a631aca1083f, the directory will be + named foo.a631aca1083f. + A snapshot of the working directory won't have a changeset ID + appended, so it would just be foo in this example. To see what + this looks like in practice, look again at the extdiff example above. Notice + that the diff has the snapshot directory names embedded in its + header. + + The extdiff command + accepts two important options. The option + lets you choose a program to view differences with, instead of + diff. With the option, + you can change the options that extdiff passes to the program + (by default, these options are + -Npru, which only make sense + if you're running diff). In other respects, + the extdiff command + acts similarly to the built-in hg + diff command: you use the same option names, syntax, + and arguments to specify the revisions you want, the files you + want, and so on. + + As an example, here's how to run the normal system + diff command, getting it to generate context + diffs (using the option) + instead of unified diffs, and five lines of context instead of + the default three (passing 5 as the argument + to the option). + + &interaction.extdiff.extdiff-ctx; + + Launching a visual diff tool is just as easy. Here's how to + launch the kdiff3 viewer. + hg extdiff -p kdiff3 -o + + If your diff viewing command can't deal with directories, + you can easily work around this with a little scripting. For an + example of such scripting in action with the mq extension and the + interdiff command, see . + + + Defining command aliases + + It can be cumbersome to remember the options to both the + extdiff command and + the diff viewer you want to use, so the extdiff extension lets you define + new commands that will invoke your diff + viewer with exactly the right options. + + All you need to do is edit your ~/.hgrc, and add a section named + extdiff. Inside this + section, you can define multiple commands. Here's how to add + a kdiff3 command. Once you've defined + this, you can type hg kdiff3 + and the extdiff extension + will run kdiff3 for you. + [extdiff] +cmd.kdiff3 = + If you leave the right hand side of the definition empty, + as above, the extdiff + extension uses the name of the command you defined as the name + of the external program to run. But these names don't have to + be the same. Here, we define a command named + hg wibble, which runs + kdiff3. + [extdiff] + cmd.wibble = kdiff3 + + You can also specify the default options that you want to + invoke your diff viewing program with. The prefix to use is + opts., followed by the name + of the command to which the options apply. This example + defines a hg vimdiff command + that runs the vim editor's + DirDiff extension. + [extdiff] + cmd.vimdiff = vim +opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' + + + + + Cherrypicking changes with the <literal + role="hg-ext">transplant</literal> extension + + Need to have a long chat with Brendan about this. + + + + Send changes via email with the <literal + role="hg-ext">patchbomb</literal> extension + + Many projects have a culture of change + review, in which people send their modifications to a + mailing list for others to read and comment on before they + commit the final version to a shared repository. Some projects + have people who act as gatekeepers; they apply changes from + other people to a repository to which those others don't have + access. + + Mercurial makes it easy to send changes over email for + review or application, via its patchbomb extension. The extension is + so named because changes are formatted as patches, and it's usual + to send one changeset per email message. Sending a long series + of changes by email is thus much like bombing the + recipient's inbox, hence patchbomb. + + As usual, the basic configuration of the patchbomb extension takes just one or + two lines in your + /.hgrc. + [extensions] +patchbomb = + Once you've enabled the extension, you will have a new + command available, named email. + + The safest and best way to invoke the email command is to + always run it first with the option. + This will show you what the command would + send, without actually sending anything. Once you've had a + quick glance over the changes and verified that you are sending + the right ones, you can rerun the same command, with the option + removed. + + The email command + accepts the same kind of revision syntax as every other + Mercurial command. For example, this command will send every + revision between 7 and tip, inclusive. + hg email -n 7:tip + You can also specify a repository to + compare with. If you provide a repository but no revisions, the + email command will + send all revisions in the local repository that are not present + in the remote repository. If you additionally specify revisions + or a branch name (the latter using the option), + this will constrain the revisions sent. + + It's perfectly safe to run the email command without the + names of the people you want to send to: if you do this, it will + just prompt you for those values interactively. (If you're + using a Linux or Unix-like system, you should have enhanced + readline-style editing capabilities when + entering those headers, too, which is useful.) + + When you are sending just one revision, the email command will by + default use the first line of the changeset description as the + subject of the single email message it sends. + + If you send multiple revisions, the email command will usually + send one message per changeset. It will preface the series with + an introductory message, in which you should describe the + purpose of the series of changes you're sending. + + + Changing the behavior of patchbombs + + Not every project has exactly the same conventions for + sending changes in email; the patchbomb extension tries to + accommodate a number of variations through command line + options. + + You can write a subject for the introductory + message on the command line using the + option. This takes one argument, the text of the subject + to use. + + To change the email address from which the + messages originate, use the + option. This takes one argument, the email address to + use. + + The default behavior is to send unified diffs + (see for a + description of the + format), one per message. You can send a binary bundle + instead with the + option. + + Unified diffs are normally prefaced with a + metadata header. You can omit this, and send unadorned + diffs, with the option. + + Diffs are normally sent inline, + in the same body part as the description of a patch. This + makes it easiest for the largest number of readers to + quote and respond to parts of a diff, as some mail clients + will only quote the first MIME body part in a message. If + you'd prefer to send the description and the diff in + separate body parts, use the + option. + + Instead of sending mail messages, you can + write them to an mbox-format mail + folder using the + option. That option takes one argument, the name of the + file to write to. + + If you would like to add a + diffstat-format summary to each patch, + and one to the introductory message, use the + option. The diffstat command displays + a table containing the name of each file patched, the + number of lines affected, and a histogram showing how much + each file is modified. This gives readers a qualitative + glance at how complex a patch is. + + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/cmdref.tex --- a/en/cmdref.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,176 +0,0 @@ -\chapter{Command reference} -\label{cmdref} - -\cmdref{add}{add files at the next commit} -\optref{add}{I}{include} -\optref{add}{X}{exclude} -\optref{add}{n}{dry-run} - -\cmdref{diff}{print changes in history or working directory} - -Show differences between revisions for the specified files or -directories, using the unified diff format. For a description of the -unified diff format, see section~\ref{sec:mq:patch}. - -By default, this command does not print diffs for files that Mercurial -considers to contain binary data. To control this behaviour, see the -\hgopt{diff}{-a} and \hgopt{diff}{--git} options. - -\subsection{Options} - -\loptref{diff}{nodates} - -Omit date and time information when printing diff headers. - -\optref{diff}{B}{ignore-blank-lines} - -Do not print changes that only insert or delete blank lines. A line -that contains only whitespace is not considered blank. - -\optref{diff}{I}{include} - -Include files and directories whose names match the given patterns. - -\optref{diff}{X}{exclude} - -Exclude files and directories whose names match the given patterns. - -\optref{diff}{a}{text} - -If this option is not specified, \hgcmd{diff} will refuse to print -diffs for files that it detects as binary. Specifying \hgopt{diff}{-a} -forces \hgcmd{diff} to treat all files as text, and generate diffs for -all of them. - -This option is useful for files that are ``mostly text'' but have a -few embedded NUL characters. If you use it on files that contain a -lot of binary data, its output will be incomprehensible. - -\optref{diff}{b}{ignore-space-change} - -Do not print a line if the only change to that line is in the amount -of white space it contains. - -\optref{diff}{g}{git} - -Print \command{git}-compatible diffs. XXX reference a format -description. - -\optref{diff}{p}{show-function} - -Display the name of the enclosing function in a hunk header, using a -simple heuristic. This functionality is enabled by default, so the -\hgopt{diff}{-p} option has no effect unless you change the value of -the \rcitem{diff}{showfunc} config item, as in the following example. -\interaction{cmdref.diff-p} - -\optref{diff}{r}{rev} - -Specify one or more revisions to compare. The \hgcmd{diff} command -accepts up to two \hgopt{diff}{-r} options to specify the revisions to -compare. - -\begin{enumerate} -\setcounter{enumi}{0} -\item Display the differences between the parent revision of the - working directory and the working directory. -\item Display the differences between the specified changeset and the - working directory. -\item Display the differences between the two specified changesets. -\end{enumerate} - -You can specify two revisions using either two \hgopt{diff}{-r} -options or revision range notation. For example, the two revision -specifications below are equivalent. -\begin{codesample2} - hg diff -r 10 -r 20 - hg diff -r10:20 -\end{codesample2} - -When you provide two revisions, Mercurial treats the order of those -revisions as significant. Thus, \hgcmdargs{diff}{-r10:20} will -produce a diff that will transform files from their contents as of -revision~10 to their contents as of revision~20, while -\hgcmdargs{diff}{-r20:10} means the opposite: the diff that will -transform files from their revision~20 contents to their revision~10 -contents. You cannot reverse the ordering in this way if you are -diffing against the working directory. - -\optref{diff}{w}{ignore-all-space} - -\cmdref{version}{print version and copyright information} - -This command displays the version of Mercurial you are running, and -its copyright license. There are four kinds of version string that -you may see. -\begin{itemize} -\item The string ``\texttt{unknown}''. This version of Mercurial was - not built in a Mercurial repository, and cannot determine its own - version. -\item A short numeric string, such as ``\texttt{1.1}''. This is a - build of a revision of Mercurial that was identified by a specific - tag in the repository where it was built. (This doesn't necessarily - mean that you're running an official release; someone else could - have added that tag to any revision in the repository where they - built Mercurial.) -\item A hexadecimal string, such as ``\texttt{875489e31abe}''. This - is a build of the given revision of Mercurial. -\item A hexadecimal string followed by a date, such as - ``\texttt{875489e31abe+20070205}''. This is a build of the given - revision of Mercurial, where the build repository contained some - local changes that had not been committed. -\end{itemize} - -\subsection{Tips and tricks} - -\subsubsection{Why do the results of \hgcmd{diff} and \hgcmd{status} - differ?} -\label{cmdref:diff-vs-status} - -When you run the \hgcmd{status} command, you'll see a list of files -that Mercurial will record changes for the next time you perform a -commit. If you run the \hgcmd{diff} command, you may notice that it -prints diffs for only a \emph{subset} of the files that \hgcmd{status} -listed. There are two possible reasons for this. - -The first is that \hgcmd{status} prints some kinds of modifications -that \hgcmd{diff} doesn't normally display. The \hgcmd{diff} command -normally outputs unified diffs, which don't have the ability to -represent some changes that Mercurial can track. Most notably, -traditional diffs can't represent a change in whether or not a file is -executable, but Mercurial records this information. - -If you use the \hgopt{diff}{--git} option to \hgcmd{diff}, it will -display \command{git}-compatible diffs that \emph{can} display this -extra information. - -The second possible reason that \hgcmd{diff} might be printing diffs -for a subset of the files displayed by \hgcmd{status} is that if you -invoke it without any arguments, \hgcmd{diff} prints diffs against the -first parent of the working directory. If you have run \hgcmd{merge} -to merge two changesets, but you haven't yet committed the results of -the merge, your working directory has two parents (use \hgcmd{parents} -to see them). While \hgcmd{status} prints modifications relative to -\emph{both} parents after an uncommitted merge, \hgcmd{diff} still -operates relative only to the first parent. You can get it to print -diffs relative to the second parent by specifying that parent with the -\hgopt{diff}{-r} option. There is no way to print diffs relative to -both parents. - -\subsubsection{Generating safe binary diffs} - -If you use the \hgopt{diff}{-a} option to force Mercurial to print -diffs of files that are either ``mostly text'' or contain lots of -binary data, those diffs cannot subsequently be applied by either -Mercurial's \hgcmd{import} command or the system's \command{patch} -command. - -If you want to generate a diff of a binary file that is safe to use as -input for \hgcmd{import}, use the \hgcmd{diff}{--git} option when you -generate the patch. The system \command{patch} command cannot handle -binary patches at all. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/collab.tex --- a/en/collab.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1118 +0,0 @@ -\chapter{Collaborating with other people} -\label{cha:collab} - -As a completely decentralised tool, Mercurial doesn't impose any -policy on how people ought to work with each other. However, if -you're new to distributed revision control, it helps to have some -tools and examples in mind when you're thinking about possible -workflow models. - -\section{Mercurial's web interface} - -Mercurial has a powerful web interface that provides several -useful capabilities. - -For interactive use, the web interface lets you browse a single -repository or a collection of repositories. You can view the history -of a repository, examine each change (comments and diffs), and view -the contents of each directory and file. - -Also for human consumption, the web interface provides an RSS feed of -the changes in a repository. This lets you ``subscribe'' to a -repository using your favourite feed reader, and be automatically -notified of activity in that repository as soon as it happens. I find -this capability much more convenient than the model of subscribing to -a mailing list to which notifications are sent, as it requires no -additional configuration on the part of whoever is serving the -repository. - -The web interface also lets remote users clone a repository, pull -changes from it, and (when the server is configured to permit it) push -changes back to it. Mercurial's HTTP tunneling protocol aggressively -compresses data, so that it works efficiently even over low-bandwidth -network connections. - -The easiest way to get started with the web interface is to use your -web browser to visit an existing repository, such as the master -Mercurial repository at -\url{http://www.selenic.com/repo/hg?style=gitweb}. - -If you're interested in providing a web interface to your own -repositories, Mercurial provides two ways to do this. The first is -using the \hgcmd{serve} command, which is best suited to short-term -``lightweight'' serving. See section~\ref{sec:collab:serve} below for -details of how to use this command. If you have a long-lived -repository that you'd like to make permanently available, Mercurial -has built-in support for the CGI (Common Gateway Interface) standard, -which all common web servers support. See -section~\ref{sec:collab:cgi} for details of CGI configuration. - -\section{Collaboration models} - -With a suitably flexible tool, making decisions about workflow is much -more of a social engineering challenge than a technical one. -Mercurial imposes few limitations on how you can structure the flow of -work in a project, so it's up to you and your group to set up and live -with a model that matches your own particular needs. - -\subsection{Factors to keep in mind} - -The most important aspect of any model that you must keep in mind is -how well it matches the needs and capabilities of the people who will -be using it. This might seem self-evident; even so, you still can't -afford to forget it for a moment. - -I once put together a workflow model that seemed to make perfect sense -to me, but that caused a considerable amount of consternation and -strife within my development team. In spite of my attempts to explain -why we needed a complex set of branches, and how changes ought to flow -between them, a few team members revolted. Even though they were -smart people, they didn't want to pay attention to the constraints we -were operating under, or face the consequences of those constraints in -the details of the model that I was advocating. - -Don't sweep foreseeable social or technical problems under the rug. -Whatever scheme you put into effect, you should plan for mistakes and -problem scenarios. Consider adding automated machinery to prevent, or -quickly recover from, trouble that you can anticipate. As an example, -if you intend to have a branch with not-for-release changes in it, -you'd do well to think early about the possibility that someone might -accidentally merge those changes into a release branch. You could -avoid this particular problem by writing a hook that prevents changes -from being merged from an inappropriate branch. - -\subsection{Informal anarchy} - -I wouldn't suggest an ``anything goes'' approach as something -sustainable, but it's a model that's easy to grasp, and it works -perfectly well in a few unusual situations. - -As one example, many projects have a loose-knit group of collaborators -who rarely physically meet each other. Some groups like to overcome -the isolation of working at a distance by organising occasional -``sprints''. In a sprint, a number of people get together in a single -location (a company's conference room, a hotel meeting room, that kind -of place) and spend several days more or less locked in there, hacking -intensely on a handful of projects. - -A sprint is the perfect place to use the \hgcmd{serve} command, since -\hgcmd{serve} does not requires any fancy server infrastructure. You -can get started with \hgcmd{serve} in moments, by reading -section~\ref{sec:collab:serve} below. Then simply tell the person -next to you that you're running a server, send the URL to them in an -instant message, and you immediately have a quick-turnaround way to -work together. They can type your URL into their web browser and -quickly review your changes; or they can pull a bugfix from you and -verify it; or they can clone a branch containing a new feature and try -it out. - -The charm, and the problem, with doing things in an ad hoc fashion -like this is that only people who know about your changes, and where -they are, can see them. Such an informal approach simply doesn't -scale beyond a handful people, because each individual needs to know -about $n$ different repositories to pull from. - -\subsection{A single central repository} - -For smaller projects migrating from a centralised revision control -tool, perhaps the easiest way to get started is to have changes flow -through a single shared central repository. This is also the -most common ``building block'' for more ambitious workflow schemes. - -Contributors start by cloning a copy of this repository. They can -pull changes from it whenever they need to, and some (perhaps all) -developers have permission to push a change back when they're ready -for other people to see it. - -Under this model, it can still often make sense for people to pull -changes directly from each other, without going through the central -repository. Consider a case in which I have a tentative bug fix, but -I am worried that if I were to publish it to the central repository, -it might subsequently break everyone else's trees as they pull it. To -reduce the potential for damage, I can ask you to clone my repository -into a temporary repository of your own and test it. This lets us put -off publishing the potentially unsafe change until it has had a little -testing. - -In this kind of scenario, people usually use the \command{ssh} -protocol to securely push changes to the central repository, as -documented in section~\ref{sec:collab:ssh}. It's also usual to -publish a read-only copy of the repository over HTTP using CGI, as in -section~\ref{sec:collab:cgi}. Publishing over HTTP satisfies the -needs of people who don't have push access, and those who want to use -web browsers to browse the repository's history. - -\subsection{Working with multiple branches} - -Projects of any significant size naturally tend to make progress on -several fronts simultaneously. In the case of software, it's common -for a project to go through periodic official releases. A release -might then go into ``maintenance mode'' for a while after its first -publication; maintenance releases tend to contain only bug fixes, not -new features. In parallel with these maintenance releases, one or -more future releases may be under development. People normally use -the word ``branch'' to refer to one of these many slightly different -directions in which development is proceeding. - -Mercurial is particularly well suited to managing a number of -simultaneous, but not identical, branches. Each ``development -direction'' can live in its own central repository, and you can merge -changes from one to another as the need arises. Because repositories -are independent of each other, unstable changes in a development -branch will never affect a stable branch unless someone explicitly -merges those changes in. - -Here's an example of how this can work in practice. Let's say you -have one ``main branch'' on a central server. -\interaction{branching.init} -People clone it, make changes locally, test them, and push them back. - -Once the main branch reaches a release milestone, you can use the -\hgcmd{tag} command to give a permanent name to the milestone -revision. -\interaction{branching.tag} -Let's say some ongoing development occurs on the main branch. -\interaction{branching.main} -Using the tag that was recorded at the milestone, people who clone -that repository at any time in the future can use \hgcmd{update} to -get a copy of the working directory exactly as it was when that tagged -revision was committed. -\interaction{branching.update} - -In addition, immediately after the main branch is tagged, someone can -then clone the main branch on the server to a new ``stable'' branch, -also on the server. -\interaction{branching.clone} - -Someone who needs to make a change to the stable branch can then clone -\emph{that} repository, make their changes, commit, and push their -changes back there. -\interaction{branching.stable} -Because Mercurial repositories are independent, and Mercurial doesn't -move changes around automatically, the stable and main branches are -\emph{isolated} from each other. The changes that you made on the -main branch don't ``leak'' to the stable branch, and vice versa. - -You'll often want all of your bugfixes on the stable branch to show up -on the main branch, too. Rather than rewrite a bugfix on the main -branch, you can simply pull and merge changes from the stable to the -main branch, and Mercurial will bring those bugfixes in for you. -\interaction{branching.merge} -The main branch will still contain changes that are not on the stable -branch, but it will also contain all of the bugfixes from the stable -branch. The stable branch remains unaffected by these changes. - -\subsection{Feature branches} - -For larger projects, an effective way to manage change is to break up -a team into smaller groups. Each group has a shared branch of its -own, cloned from a single ``master'' branch used by the entire -project. People working on an individual branch are typically quite -isolated from developments on other branches. - -\begin{figure}[ht] - \centering - \grafix{feature-branches} - \caption{Feature branches} - \label{fig:collab:feature-branches} -\end{figure} - -When a particular feature is deemed to be in suitable shape, someone -on that feature team pulls and merges from the master branch into the -feature branch, then pushes back up to the master branch. - -\subsection{The release train} - -Some projects are organised on a ``train'' basis: a release is -scheduled to happen every few months, and whatever features are ready -when the ``train'' is ready to leave are allowed in. - -This model resembles working with feature branches. The difference is -that when a feature branch misses a train, someone on the feature team -pulls and merges the changes that went out on that train release into -the feature branch, and the team continues its work on top of that -release so that their feature can make the next release. - -\subsection{The Linux kernel model} - -The development of the Linux kernel has a shallow hierarchical -structure, surrounded by a cloud of apparent chaos. Because most -Linux developers use \command{git}, a distributed revision control -tool with capabilities similar to Mercurial, it's useful to describe -the way work flows in that environment; if you like the ideas, the -approach translates well across tools. - -At the center of the community sits Linus Torvalds, the creator of -Linux. He publishes a single source repository that is considered the -``authoritative'' current tree by the entire developer community. -Anyone can clone Linus's tree, but he is very choosy about whose trees -he pulls from. - -Linus has a number of ``trusted lieutenants''. As a general rule, he -pulls whatever changes they publish, in most cases without even -reviewing those changes. Some of those lieutenants are generally -agreed to be ``maintainers'', responsible for specific subsystems -within the kernel. If a random kernel hacker wants to make a change -to a subsystem that they want to end up in Linus's tree, they must -find out who the subsystem's maintainer is, and ask that maintainer to -take their change. If the maintainer reviews their changes and agrees -to take them, they'll pass them along to Linus in due course. - -Individual lieutenants have their own approaches to reviewing, -accepting, and publishing changes; and for deciding when to feed them -to Linus. In addition, there are several well known branches that -people use for different purposes. For example, a few people maintain -``stable'' repositories of older versions of the kernel, to which they -apply critical fixes as needed. Some maintainers publish multiple -trees: one for experimental changes; one for changes that they are -about to feed upstream; and so on. Others just publish a single -tree. - -This model has two notable features. The first is that it's ``pull -only''. You have to ask, convince, or beg another developer to take a -change from you, because there are almost no trees to which more than -one person can push, and there's no way to push changes into a tree -that someone else controls. - -The second is that it's based on reputation and acclaim. If you're an -unknown, Linus will probably ignore changes from you without even -responding. But a subsystem maintainer will probably review them, and -will likely take them if they pass their criteria for suitability. -The more ``good'' changes you contribute to a maintainer, the more -likely they are to trust your judgment and accept your changes. If -you're well-known and maintain a long-lived branch for something Linus -hasn't yet accepted, people with similar interests may pull your -changes regularly to keep up with your work. - -Reputation and acclaim don't necessarily cross subsystem or ``people'' -boundaries. If you're a respected but specialised storage hacker, and -you try to fix a networking bug, that change will receive a level of -scrutiny from a network maintainer comparable to a change from a -complete stranger. - -To people who come from more orderly project backgrounds, the -comparatively chaotic Linux kernel development process often seems -completely insane. It's subject to the whims of individuals; people -make sweeping changes whenever they deem it appropriate; and the pace -of development is astounding. And yet Linux is a highly successful, -well-regarded piece of software. - -\subsection{Pull-only versus shared-push collaboration} - -A perpetual source of heat in the open source community is whether a -development model in which people only ever pull changes from others -is ``better than'' one in which multiple people can push changes to a -shared repository. - -Typically, the backers of the shared-push model use tools that -actively enforce this approach. If you're using a centralised -revision control tool such as Subversion, there's no way to make a -choice over which model you'll use: the tool gives you shared-push, -and if you want to do anything else, you'll have to roll your own -approach on top (such as applying a patch by hand). - -A good distributed revision control tool, such as Mercurial, will -support both models. You and your collaborators can then structure -how you work together based on your own needs and preferences, not on -what contortions your tools force you into. - -\subsection{Where collaboration meets branch management} - -Once you and your team set up some shared repositories and start -propagating changes back and forth between local and shared repos, you -begin to face a related, but slightly different challenge: that of -managing the multiple directions in which your team may be moving at -once. Even though this subject is intimately related to how your team -collaborates, it's dense enough to merit treatment of its own, in -chapter~\ref{chap:branch}. - -\section{The technical side of sharing} - -The remainder of this chapter is devoted to the question of serving -data to your collaborators. - -\section{Informal sharing with \hgcmd{serve}} -\label{sec:collab:serve} - -Mercurial's \hgcmd{serve} command is wonderfully suited to small, -tight-knit, and fast-paced group environments. It also provides a -great way to get a feel for using Mercurial commands over a network. - -Run \hgcmd{serve} inside a repository, and in under a second it will -bring up a specialised HTTP server; this will accept connections from -any client, and serve up data for that repository until you terminate -it. Anyone who knows the URL of the server you just started, and can -talk to your computer over the network, can then use a web browser or -Mercurial to read data from that repository. A URL for a -\hgcmd{serve} instance running on a laptop is likely to look something -like \Verb|http://my-laptop.local:8000/|. - -The \hgcmd{serve} command is \emph{not} a general-purpose web server. -It can do only two things: -\begin{itemize} -\item Allow people to browse the history of the repository it's - serving, from their normal web browsers. -\item Speak Mercurial's wire protocol, so that people can - \hgcmd{clone} or \hgcmd{pull} changes from that repository. -\end{itemize} -In particular, \hgcmd{serve} won't allow remote users to \emph{modify} -your repository. It's intended for read-only use. - -If you're getting started with Mercurial, there's nothing to prevent -you from using \hgcmd{serve} to serve up a repository on your own -computer, then use commands like \hgcmd{clone}, \hgcmd{incoming}, and -so on to talk to that server as if the repository was hosted remotely. -This can help you to quickly get acquainted with using commands on -network-hosted repositories. - -\subsection{A few things to keep in mind} - -Because it provides unauthenticated read access to all clients, you -should only use \hgcmd{serve} in an environment where you either don't -care, or have complete control over, who can access your network and -pull data from your repository. - -The \hgcmd{serve} command knows nothing about any firewall software -you might have installed on your system or network. It cannot detect -or control your firewall software. If other people are unable to talk -to a running \hgcmd{serve} instance, the second thing you should do -(\emph{after} you make sure that they're using the correct URL) is -check your firewall configuration. - -By default, \hgcmd{serve} listens for incoming connections on -port~8000. If another process is already listening on the port you -want to use, you can specify a different port to listen on using the -\hgopt{serve}{-p} option. - -Normally, when \hgcmd{serve} starts, it prints no output, which can be -a bit unnerving. If you'd like to confirm that it is indeed running -correctly, and find out what URL you should send to your -collaborators, start it with the \hggopt{-v} option. - -\section{Using the Secure Shell (ssh) protocol} -\label{sec:collab:ssh} - -You can pull and push changes securely over a network connection using -the Secure Shell (\texttt{ssh}) protocol. To use this successfully, -you may have to do a little bit of configuration on the client or -server sides. - -If you're not familiar with ssh, it's a network protocol that lets you -securely communicate with another computer. To use it with Mercurial, -you'll be setting up one or more user accounts on a server so that -remote users can log in and execute commands. - -(If you \emph{are} familiar with ssh, you'll probably find some of the -material that follows to be elementary in nature.) - -\subsection{How to read and write ssh URLs} - -An ssh URL tends to look like this: -\begin{codesample2} - ssh://bos@hg.serpentine.com:22/hg/hgbook -\end{codesample2} -\begin{enumerate} -\item The ``\texttt{ssh://}'' part tells Mercurial to use the ssh - protocol. -\item The ``\texttt{bos@}'' component indicates what username to log - into the server as. You can leave this out if the remote username - is the same as your local username. -\item The ``\texttt{hg.serpentine.com}'' gives the hostname of the - server to log into. -\item The ``:22'' identifies the port number to connect to the server - on. The default port is~22, so you only need to specify this part - if you're \emph{not} using port~22. -\item The remainder of the URL is the local path to the repository on - the server. -\end{enumerate} - -There's plenty of scope for confusion with the path component of ssh -URLs, as there is no standard way for tools to interpret it. Some -programs behave differently than others when dealing with these paths. -This isn't an ideal situation, but it's unlikely to change. Please -read the following paragraphs carefully. - -Mercurial treats the path to a repository on the server as relative to -the remote user's home directory. For example, if user \texttt{foo} -on the server has a home directory of \dirname{/home/foo}, then an ssh -URL that contains a path component of \dirname{bar} -\emph{really} refers to the directory \dirname{/home/foo/bar}. - -If you want to specify a path relative to another user's home -directory, you can use a path that starts with a tilde character -followed by the user's name (let's call them \texttt{otheruser}), like -this. -\begin{codesample2} - ssh://server/~otheruser/hg/repo -\end{codesample2} - -And if you really want to specify an \emph{absolute} path on the -server, begin the path component with two slashes, as in this example. -\begin{codesample2} - ssh://server//absolute/path -\end{codesample2} - -\subsection{Finding an ssh client for your system} - -Almost every Unix-like system comes with OpenSSH preinstalled. If -you're using such a system, run \Verb|which ssh| to find out if -the \command{ssh} command is installed (it's usually in -\dirname{/usr/bin}). In the unlikely event that it isn't present, -take a look at your system documentation to figure out how to install -it. - -On Windows, you'll first need to 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 5981a0f7540a -r 019040fbf5f5 en/concepts.tex --- a/en/concepts.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,577 +0,0 @@ -\chapter{Behind the scenes} -\label{chap:concepts} - -Unlike many revision control systems, the concepts upon which -Mercurial is built are simple enough that it's easy to understand how -the software really works. Knowing this certainly isn't necessary, -but I find it useful to have a ``mental model'' of what's going on. - -This understanding gives me confidence that Mercurial has been -carefully designed to be both \emph{safe} and \emph{efficient}. And -just as importantly, if it's easy for me to retain a good idea of what -the software is doing when I perform a revision control task, I'm less -likely to be surprised by its behaviour. - -In this chapter, we'll initially cover the core concepts behind -Mercurial's design, then continue to discuss some of the interesting -details of its implementation. - -\section{Mercurial's historical record} - -\subsection{Tracking the history of a single file} - -When Mercurial tracks modifications to a file, it stores the history -of that file in a metadata object called a \emph{filelog}. Each entry -in the filelog contains enough information to reconstruct one revision -of the file that is being tracked. Filelogs are stored as files in -the \sdirname{.hg/store/data} directory. A filelog contains two kinds -of information: revision data, and an index to help Mercurial to find -a revision efficiently. - -A file that is large, or has a lot of history, has its filelog stored -in separate data (``\texttt{.d}'' suffix) and index (``\texttt{.i}'' -suffix) files. For small files without much history, the revision -data and index are combined in a single ``\texttt{.i}'' file. The -correspondence between a file in the working directory and the filelog -that tracks its history in the repository is illustrated in -figure~\ref{fig:concepts:filelog}. - -\begin{figure}[ht] - \centering - \grafix{filelog} - \caption{Relationships between files in working directory and - filelogs in repository} - \label{fig:concepts:filelog} -\end{figure} - -\subsection{Managing tracked files} - -Mercurial uses a structure called a \emph{manifest} to collect -together information about the files that it tracks. Each entry in -the manifest contains information about the files present in a single -changeset. An entry records which files are present in the changeset, -the revision of each file, and a few other pieces of file metadata. - -\subsection{Recording changeset information} - -The \emph{changelog} contains information about each changeset. Each -revision records who committed a change, the changeset comment, other -pieces of changeset-related information, and the revision of the -manifest to use. - -\subsection{Relationships between revisions} - -Within a changelog, a manifest, or a filelog, each revision stores a -pointer to its immediate parent (or to its two parents, if it's a -merge revision). As I mentioned above, there are also relationships -between revisions \emph{across} these structures, and they are -hierarchical in nature. - -For every changeset in a repository, there is exactly one revision -stored in the changelog. Each revision of the changelog contains a -pointer to a single revision of the manifest. A revision of the -manifest stores a pointer to a single revision of each filelog tracked -when that changeset was created. These relationships are illustrated -in figure~\ref{fig:concepts:metadata}. - -\begin{figure}[ht] - \centering - \grafix{metadata} - \caption{Metadata relationships} - \label{fig:concepts:metadata} -\end{figure} - -As the illustration shows, there is \emph{not} a ``one to one'' -relationship between revisions in the changelog, manifest, or filelog. -If the manifest hasn't changed between two changesets, the changelog -entries for those changesets will point to the same revision of the -manifest. If a file that Mercurial tracks hasn't changed between two -changesets, the entry for that file in the two revisions of the -manifest will point to the same revision of its filelog. - -\section{Safe, efficient storage} - -The underpinnings of changelogs, manifests, and filelogs are provided -by a single structure called the \emph{revlog}. - -\subsection{Efficient storage} - -The revlog provides efficient storage of revisions using a -\emph{delta} mechanism. Instead of storing a complete copy of a file -for each revision, it stores the changes needed to transform an older -revision into the new revision. For many kinds of file data, these -deltas are typically a fraction of a percent of the size of a full -copy of a file. - -Some obsolete revision control systems can only work with deltas of -text files. They must either store binary files as complete snapshots -or encoded into a text representation, both of which are wasteful -approaches. Mercurial can efficiently handle deltas of files with -arbitrary binary contents; it doesn't need to treat text as special. - -\subsection{Safe operation} -\label{sec:concepts:txn} - -Mercurial only ever \emph{appends} data to the end of a revlog file. -It never modifies a section of a file after it has written it. This -is both more robust and efficient than schemes that need to modify or -rewrite data. - -In addition, Mercurial treats every write as part of a -\emph{transaction} that can span a number of files. A transaction is -\emph{atomic}: either the entire transaction succeeds and its effects -are all visible to readers in one go, or the whole thing is undone. -This guarantee of atomicity means that if you're running two copies of -Mercurial, where one is reading data and one is writing it, the reader -will never see a partially written result that might confuse it. - -The fact that Mercurial only appends to files makes it easier to -provide this transactional guarantee. The easier it is to do stuff -like this, the more confident you should be that it's done correctly. - -\subsection{Fast retrieval} - -Mercurial cleverly avoids a pitfall common to all earlier -revision control systems: the problem of \emph{inefficient retrieval}. -Most revision control systems store the contents of a revision as an -incremental series of modifications against a ``snapshot''. To -reconstruct a specific revision, you must first read the snapshot, and -then every one of the revisions between the snapshot and your target -revision. The more history that a file accumulates, the more -revisions you must read, hence the longer it takes to reconstruct a -particular revision. - -\begin{figure}[ht] - \centering - \grafix{snapshot} - \caption{Snapshot of a revlog, with incremental deltas} - \label{fig:concepts:snapshot} -\end{figure} - -The innovation that Mercurial applies to this problem is simple but -effective. Once the cumulative amount of delta information stored -since the last snapshot exceeds a fixed threshold, it stores a new -snapshot (compressed, of course), instead of another delta. This -makes it possible to reconstruct \emph{any} revision of a file -quickly. This approach works so well that it has since been copied by -several other revision control systems. - -Figure~\ref{fig:concepts:snapshot} illustrates the idea. In an entry -in a revlog's index file, Mercurial stores the range of entries from -the data file that it must read to reconstruct a particular revision. - -\subsubsection{Aside: the influence of video compression} - -If you're familiar with video compression or have ever watched a TV -feed through a digital cable or satellite service, you may know that -most video compression schemes store each frame of video as a delta -against its predecessor frame. In addition, these schemes use -``lossy'' compression techniques to increase the compression ratio, so -visual errors accumulate over the course of a number of inter-frame -deltas. - -Because it's possible for a video stream to ``drop out'' occasionally -due to signal glitches, and to limit the accumulation of artefacts -introduced by the lossy compression process, video encoders -periodically insert a complete frame (called a ``key frame'') into the -video stream; the next delta is generated against that frame. This -means that if the video signal gets interrupted, it will resume once -the next key frame is received. Also, the accumulation of encoding -errors restarts anew with each key frame. - -\subsection{Identification and strong integrity} - -Along with delta or snapshot information, a revlog entry contains a -cryptographic hash of the data that it represents. This makes it -difficult to forge the contents of a revision, and easy to detect -accidental corruption. - -Hashes provide more than a mere check against corruption; they are -used as the identifiers for revisions. The changeset identification -hashes that you see as an end user are from revisions of the -changelog. Although filelogs and the manifest also use hashes, -Mercurial only uses these behind the scenes. - -Mercurial verifies that hashes are correct when it retrieves file -revisions and when it pulls changes from another repository. If it -encounters an integrity problem, it will complain and stop whatever -it's doing. - -In addition to the effect it has on retrieval efficiency, Mercurial's -use of periodic snapshots makes it more robust against partial data -corruption. If a revlog becomes partly corrupted due to a hardware -error or system bug, it's often possible to reconstruct some or most -revisions from the uncorrupted sections of the revlog, both before and -after the corrupted section. This would not be possible with a -delta-only storage model. - -\section{Revision history, branching, - and merging} - -Every entry in a Mercurial revlog knows the identity of its immediate -ancestor revision, usually referred to as its \emph{parent}. In fact, -a revision contains room for not one parent, but two. Mercurial uses -a special hash, called the ``null ID'', to represent the idea ``there -is no parent here''. This hash is simply a string of zeroes. - -In figure~\ref{fig:concepts:revlog}, you can see an example of the -conceptual structure of a revlog. Filelogs, manifests, and changelogs -all have this same structure; they differ only in the kind of data -stored in each delta or snapshot. - -The first revision in a revlog (at the bottom of the image) has the -null ID in both of its parent slots. For a ``normal'' revision, its -first parent slot contains the ID of its parent revision, and its -second contains the null ID, indicating that the revision has only one -real parent. Any two revisions that have the same parent ID are -branches. A revision that represents a merge between branches has two -normal revision IDs in its parent slots. - -\begin{figure}[ht] - \centering - \grafix{revlog} - \caption{} - \label{fig:concepts:revlog} -\end{figure} - -\section{The working directory} - -In the working directory, Mercurial stores a snapshot of the files -from the repository as of a particular changeset. - -The working directory ``knows'' which changeset it contains. When you -update the working directory to contain a particular changeset, -Mercurial looks up the appropriate revision of the manifest to find -out which files it was tracking at the time that changeset was -committed, and which revision of each file was then current. It then -recreates a copy of each of those files, with the same contents it had -when the changeset was committed. - -The \emph{dirstate} contains Mercurial's knowledge of the working -directory. This details which changeset the working directory is -updated to, and all of the files that Mercurial is tracking in the -working directory. - -Just as a revision of a revlog has room for two parents, so that it -can represent either a normal revision (with one parent) or a merge of -two earlier revisions, the dirstate has slots for two parents. When -you use the \hgcmd{update} command, the changeset that you update to -is stored in the ``first parent'' slot, and the null ID in the second. -When you \hgcmd{merge} with another changeset, the first parent -remains unchanged, and the second parent is filled in with the -changeset you're merging with. The \hgcmd{parents} command tells you -what the parents of the dirstate are. - -\subsection{What happens when you commit} - -The dirstate stores parent information for more than just book-keeping -purposes. Mercurial uses the parents of the dirstate as \emph{the - parents of a new changeset} when you perform a commit. - -\begin{figure}[ht] - \centering - \grafix{wdir} - \caption{The working directory can have two parents} - \label{fig:concepts:wdir} -\end{figure} - -Figure~\ref{fig:concepts:wdir} shows the normal state of the working -directory, where it has a single changeset as parent. That changeset -is the \emph{tip}, the newest changeset in the repository that has no -children. - -\begin{figure}[ht] - \centering - \grafix{wdir-after-commit} - \caption{The working directory gains new parents after a commit} - \label{fig:concepts:wdir-after-commit} -\end{figure} - -It's useful to think of the working directory as ``the changeset I'm -about to commit''. Any files that you tell Mercurial that you've -added, removed, renamed, or copied will be reflected in that -changeset, as will modifications to any files that Mercurial is -already tracking; the new changeset will have the parents of the -working directory as its parents. - -After a commit, Mercurial will update the parents of the working -directory, so that the first parent is the ID of the new changeset, -and the second is the null ID. This is shown in -figure~\ref{fig:concepts:wdir-after-commit}. Mercurial doesn't touch -any of the files in the working directory when you commit; it just -modifies the dirstate to note its new parents. - -\subsection{Creating a new head} - -It's perfectly normal to update the working directory to a changeset -other than the current tip. For example, you might want to know what -your project looked like last Tuesday, or you could be looking through -changesets to see which one introduced a bug. In cases like this, the -natural thing to do is update the working directory to the changeset -you're interested in, and then examine the files in the working -directory directly to see their contents as they 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 5981a0f7540a -r 019040fbf5f5 en/daily.tex --- a/en/daily.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,381 +0,0 @@ -\chapter{Mercurial in daily use} -\label{chap:daily} - -\section{Telling Mercurial which files to track} - -Mercurial does not work with files in your repository unless you tell -it to manage them. The \hgcmd{status} command will tell you which -files Mercurial doesn't know about; it uses a ``\texttt{?}'' to -display such files. - -To tell Mercurial to track a file, use the \hgcmd{add} command. Once -you have added a file, the entry in the output of \hgcmd{status} for -that file changes from ``\texttt{?}'' to ``\texttt{A}''. -\interaction{daily.files.add} - -After you run a \hgcmd{commit}, the files that you added before the -commit will no longer be listed in the output of \hgcmd{status}. The -reason for this is that \hgcmd{status} only tells you about -``interesting'' files---those that you have modified or told Mercurial -to do something with---by default. If you have a repository that -contains thousands of files, you will rarely want to know about files -that Mercurial is tracking, but that have not changed. (You can still -get this information; we'll return to this later.) - -Once you add a file, Mercurial doesn't do anything with it -immediately. Instead, it will take a snapshot of the file's state the -next time you perform a commit. It will then continue to track the -changes you make to the file every time you commit, until you remove -the file. - -\subsection{Explicit versus implicit file naming} - -A useful behaviour that Mercurial has is that if you pass the name of -a directory to a command, every Mercurial command will treat this as -``I want to operate on every file in this directory and its -subdirectories''. -\interaction{daily.files.add-dir} -Notice in this example that Mercurial printed the names of the files -it added, whereas it didn't do so when we added the file named -\filename{a} in the earlier example. - -What's going on is that in the former case, we explicitly named the -file to add on the command line, so the assumption that Mercurial -makes in such cases is that you know what you were doing, and it -doesn't print any output. - -However, when we \emph{imply} the names of files by giving the name of -a directory, Mercurial takes the extra step of printing the name of -each file that it does something with. This makes it more clear what -is happening, and reduces the likelihood of a silent and nasty -surprise. This behaviour is common to most Mercurial commands. - -\subsection{Aside: Mercurial tracks files, not directories} - -Mercurial does not track directory information. Instead, it tracks -the path to a file. Before creating a file, it first creates any -missing directory components of the path. After it deletes a file, it -then deletes any empty directories that were in the deleted file's -path. This sounds like a trivial distinction, but it has one minor -practical consequence: it is not possible to represent a completely -empty directory in Mercurial. - -Empty directories are rarely useful, and there are unintrusive -workarounds that you can use to achieve an appropriate effect. The -developers of Mercurial thus felt that the complexity that would be -required to manage empty directories was not worth the limited benefit -this feature would bring. - -If you need an empty directory in your repository, there are a few -ways to achieve this. One is to create a directory, then \hgcmd{add} a -``hidden'' file to that directory. On Unix-like systems, any file -name that begins with a period (``\texttt{.}'') is treated as hidden -by most commands and GUI tools. This approach is illustrated in -figure~\ref{ex:daily:hidden}. - -\begin{figure}[ht] - \interaction{daily.files.hidden} - \caption{Simulating an empty directory using a hidden file} - \label{ex:daily:hidden} -\end{figure} - -Another way to tackle a need for an empty directory is to simply -create one in your automated build scripts before they will need it. - -\section{How to stop tracking a file} - -Once you decide that a file no longer belongs in your repository, use -the \hgcmd{remove} command; this deletes the file, and tells Mercurial -to stop tracking it. A removed file is represented in the output of -\hgcmd{status} with a ``\texttt{R}''. -\interaction{daily.files.remove} - -After you \hgcmd{remove} a file, Mercurial will no longer track -changes to that file, even if you recreate a file with the same name -in your working directory. If you do recreate a file with the same -name and want Mercurial to track the new file, simply \hgcmd{add} it. -Mercurial will know that the newly added file is not related to the -old file of the same name. - -\subsection{Removing a file does not affect its history} - -It is important to understand that removing a file has only two -effects. -\begin{itemize} -\item It removes the current version of the file from the working - directory. -\item It stops Mercurial from tracking changes to the file, from the - time of the next commit. -\end{itemize} -Removing a file \emph{does not} in any way alter the \emph{history} of -the file. - -If you update the working directory to a changeset in which a file -that you have removed was still tracked, it will reappear in the -working directory, with the contents it had when you committed that -changeset. If you then update the working directory to a later -changeset, in which the file had been removed, Mercurial will once -again remove the file from the working directory. - -\subsection{Missing files} - -Mercurial considers a file that you have deleted, but not used -\hgcmd{remove} to delete, to be \emph{missing}. A missing file is -represented with ``\texttt{!}'' in the output of \hgcmd{status}. -Mercurial commands will not generally do anything with missing files. -\interaction{daily.files.missing} - -If your repository contains a file that \hgcmd{status} reports as -missing, and you want the file to stay gone, you can run -\hgcmdargs{remove}{\hgopt{remove}{--after}} at any time later on, to -tell Mercurial that you really did mean to remove the file. -\interaction{daily.files.remove-after} - -On the other hand, if you deleted the missing file by accident, use -\hgcmdargs{revert}{\emph{filename}} to recover the file. It will -reappear, in unmodified form. -\interaction{daily.files.recover-missing} - -\subsection{Aside: why tell Mercurial explicitly to - remove a file?} - -You might wonder why Mercurial requires you to explicitly tell it that -you are deleting a file. Early during the development of Mercurial, -it let you delete a file however you pleased; Mercurial would notice -the absence of the file automatically when you next ran a -\hgcmd{commit}, and stop tracking the file. In practice, this made it -too easy to accidentally remove a file without noticing. - -\subsection{Useful shorthand---adding and removing files - in one step} - -Mercurial offers a combination command, \hgcmd{addremove}, that adds -untracked files and marks missing files as removed. -\interaction{daily.files.addremove} -The \hgcmd{commit} command also provides a \hgopt{commit}{-A} option -that performs this same add-and-remove, immediately followed by a -commit. -\interaction{daily.files.commit-addremove} - -\section{Copying files} - -Mercurial provides a \hgcmd{copy} command that lets you make a new -copy of a file. When you copy a file using this command, Mercurial -makes a record of the fact that the new file is a copy of the original -file. It treats these copied files specially when you merge your work -with someone else's. - -\subsection{The results of copying during a merge} - -What happens during a merge is that changes ``follow'' a copy. To -best illustrate what this means, let's create an example. We'll start -with the usual tiny repository that contains a single file. -\interaction{daily.copy.init} -We need to do some work in parallel, so that we'll have something to -merge. So let's clone our repository. -\interaction{daily.copy.clone} -Back in our initial repository, let's use the \hgcmd{copy} command to -make a copy of the first file we created. -\interaction{daily.copy.copy} - -If we look at the output of the \hgcmd{status} command afterwards, the -copied file looks just like a normal added file. -\interaction{daily.copy.status} -But if we pass the \hgopt{status}{-C} option to \hgcmd{status}, it -prints another line of output: this is the file that our newly-added -file was copied \emph{from}. -\interaction{daily.copy.status-copy} - -Now, back in the repository we cloned, let's make a change in -parallel. We'll add a line of content to the original file that we -created. -\interaction{daily.copy.other} -Now we have a modified \filename{file} in this repository. When we -pull the changes from the first repository, and merge the two heads, -Mercurial will propagate the changes that we made locally to -\filename{file} into its copy, \filename{new-file}. -\interaction{daily.copy.merge} - -\subsection{Why should changes follow copies?} -\label{sec:daily:why-copy} - -This behaviour, of changes to a file propagating out to copies of the -file, might seem esoteric, but in most cases it's highly desirable. - -First of all, remember that this propagation \emph{only} happens when -you merge. So if you \hgcmd{copy} a file, and subsequently modify the -original file during the normal course of your work, nothing will -happen. - -The second thing to know is that modifications will only propagate -across a copy as long as the repository that you're pulling changes -from \emph{doesn't know} about the copy. - -The reason that Mercurial does this is as follows. Let's say I make -an important bug fix in a source file, and commit my changes. -Meanwhile, you've decided to \hgcmd{copy} the file in your repository, -without knowing about the bug or having seen the fix, and you have -started hacking on your copy of the file. - -If you pulled and merged my changes, and Mercurial \emph{didn't} -propagate changes across copies, your source file would now contain -the bug, and unless you remembered to propagate the bug fix by hand, -the bug would \emph{remain} in your copy of the file. - -By automatically propagating the change that fixed the bug from the -original file to the copy, Mercurial prevents this class of problem. -To my knowledge, Mercurial is the \emph{only} revision control system -that propagates changes across copies like this. - -Once your change history has a record that the copy and subsequent -merge occurred, there's usually no further need to propagate changes -from the original file to the copied file, and that's why Mercurial -only propagates changes across copies until this point, and no -further. - -\subsection{How to make changes \emph{not} follow a copy} - -If, for some reason, you decide that this business of automatically -propagating changes across copies is not for you, simply use your -system's normal file copy command (on Unix-like systems, that's -\command{cp}) to make a copy of a file, then \hgcmd{add} the new copy -by hand. Before you do so, though, please do reread -section~\ref{sec:daily:why-copy}, and make an informed decision that -this behaviour is not appropriate to your specific case. - -\subsection{Behaviour of the \hgcmd{copy} command} - -When you use the \hgcmd{copy} command, Mercurial makes a copy of each -source file as it currently stands in the working directory. This -means that if you make some modifications to a file, then \hgcmd{copy} -it without first having committed those changes, the new copy will -also contain the modifications you have made up until that point. (I -find this behaviour a little counterintuitive, which is why I mention -it here.) - -The \hgcmd{copy} command acts similarly to the Unix \command{cp} -command (you can use the \hgcmd{cp} alias if you prefer). The last -argument is the \emph{destination}, and all prior arguments are -\emph{sources}. If you pass it a single file as the source, and the -destination does not exist, it creates a new file with that name. -\interaction{daily.copy.simple} -If the destination is a directory, Mercurial copies its sources into -that directory. -\interaction{daily.copy.dir-dest} -Copying a directory is recursive, and preserves the directory -structure of the source. -\interaction{daily.copy.dir-src} -If the source and destination are both directories, the source tree is -recreated in the destination directory. -\interaction{daily.copy.dir-src-dest} - -As with the \hgcmd{rename} command, if you copy a file manually and -then want Mercurial to know that you've copied the file, simply use -the \hgopt{copy}{--after} option to \hgcmd{copy}. -\interaction{daily.copy.after} - -\section{Renaming files} - -It's rather more common to need to rename a file than to make a copy -of it. The reason I discussed the \hgcmd{copy} command before talking -about renaming files is that Mercurial treats a rename in essentially -the same way as a copy. Therefore, knowing what Mercurial does when -you copy a file tells you what to expect when you rename a file. - -When you use the \hgcmd{rename} command, Mercurial makes a copy of -each source file, then deletes it and marks the file as removed. -\interaction{daily.rename.rename} -The \hgcmd{status} command shows the newly copied file as added, and -the copied-from file as removed. -\interaction{daily.rename.status} -As with the results of a \hgcmd{copy}, we must use the -\hgopt{status}{-C} option to \hgcmd{status} to see that the added file -is really being tracked by Mercurial as a copy of the original, now -removed, file. -\interaction{daily.rename.status-copy} - -As with \hgcmd{remove} and \hgcmd{copy}, you can tell Mercurial about -a rename after the fact using the \hgopt{rename}{--after} option. In -most other respects, the behaviour of the \hgcmd{rename} command, and -the options it accepts, are similar to the \hgcmd{copy} command. - -\subsection{Renaming files and merging changes} - -Since Mercurial's rename is implemented as copy-and-remove, the same -propagation of changes happens when you merge after a rename as after -a copy. - -If I modify a file, and you rename it to a new name, and then we merge -our respective changes, my modifications to the file under its -original name will be propagated into the file under its new name. -(This is something you might expect to ``simply work,'' but not all -revision control systems actually do this.) - -Whereas having changes follow a copy is a feature where you can -perhaps nod and say ``yes, that might be useful,'' it should be clear -that having them follow a rename is definitely important. Without -this facility, it would simply be too easy for changes to become -orphaned when files are renamed. - -\subsection{Divergent renames and merging} - -The case of diverging names occurs when two developers start with a -file---let's call it \filename{foo}---in their respective -repositories. - -\interaction{rename.divergent.clone} -Anne renames the file to \filename{bar}. -\interaction{rename.divergent.rename.anne} -Meanwhile, Bob renames it to \filename{quux}. -\interaction{rename.divergent.rename.bob} - -I like to think of this as a conflict because each developer has -expressed different intentions about what the file ought to be named. - -What do you think should happen when they merge their work? -Mercurial's actual behaviour is that it always preserves \emph{both} -names when it merges changesets that contain divergent renames. -\interaction{rename.divergent.merge} - -Notice that Mercurial does warn about the divergent renames, but it -leaves it up to you to do something about the divergence after the merge. - -\subsection{Convergent renames and merging} - -Another kind of rename conflict occurs when two people choose to -rename different \emph{source} files to the same \emph{destination}. -In this case, Mercurial runs its normal merge machinery, and lets you -guide it to a suitable resolution. - -\subsection{Other name-related corner cases} - -Mercurial has a longstanding bug in which it fails to handle a merge -where one side has a file with a given name, while another has a -directory with the same name. This is documented as~\bug{29}. -\interaction{issue29.go} - -\section{Recovering from mistakes} - -Mercurial has some useful commands that will help you to recover from -some common mistakes. - -The \hgcmd{revert} command lets you undo changes that you have made to -your working directory. For example, if you \hgcmd{add} a file by -accident, just run \hgcmd{revert} with the name of the file you added, -and while the file won't be touched in any way, it won't be tracked -for adding by Mercurial any longer, either. You can also use -\hgcmd{revert} to get rid of erroneous changes to a file. - -It's useful to remember that the \hgcmd{revert} command is useful for -changes that you have not yet committed. Once you've committed a -change, if you decide it was a mistake, you can still do something -about it, though your options may be more limited. - -For more information about the \hgcmd{revert} command, and details -about how to deal with changes you have already committed, see -chapter~\ref{chap:undo}. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/auto-snippets.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/auto-snippets.xml Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.init.out --- a/en/examples/backout.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.backout.out --- a/en/examples/backout.manual.backout.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.cat.out --- a/en/examples/backout.manual.cat.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cat myfile} -first change diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.clone.out --- a/en/examples/backout.manual.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.heads.out --- a/en/examples/backout.manual.heads.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.log.out --- a/en/examples/backout.manual.log.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \textbf{hg log --style compact} -3[tip]:1 - back out second change - -2 - third change - -1 - second change - -0 - first change - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.merge.out --- a/en/examples/backout.manual.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.manual.parents.out --- a/en/examples/backout.manual.parents.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \textbf{hg parents} -changeset: -tag: tip -parent: -user: Bryan O'Sullivan - -summary: back out second change - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.non-tip.backout.out --- a/en/examples/backout.non-tip.backout.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.non-tip.cat.out --- a/en/examples/backout.non-tip.cat.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cat myfile} -first change -third change diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.non-tip.clone.out --- a/en/examples/backout.non-tip.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/backout.simple.log.out --- a/en/examples/backout.simple.log.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \textbf{hg log --style compact} -2[tip] - back out second change - -1 - second change - -0 - first change - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/backout.simple.out --- a/en/examples/backout.simple.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{hg backout -m 'back out second change' tip} -reverting myfile -changeset backs out changeset -$ \textbf{cat myfile} -first change diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect --- a/en/examples/bisect Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/bisect Tue Apr 21 00:36:40 2009 +0900 @@ -1,7 +1,11 @@ #!/bin/bash +if hg -v | head -1 | grep -e "version 0.*" +then +#On mercurial 1.0 and later bisect is a builtin echo '[extensions]' >> $HGRC echo 'hbisect =' >> $HGRC +fi # XXX There's some kind of horrible nondeterminism in the execution of # bisect at the moment. Ugh. @@ -33,15 +37,15 @@ #$ name: search.init -hg bisect --init +hg bisect init #$ name: search.bad-init -hg bisect --bad +hg bisect bad #$ name: search.good-init -hg bisect --good 10 +hg bisect good 10 #$ name: search.step1 @@ -66,7 +70,7 @@ fi echo this revision is $result - hg bisect --$result + hg bisect $result } #$ name: search.step2 @@ -81,7 +85,7 @@ #$ name: search.reset -hg bisect --reset +hg bisect reset #$ name: diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.commits.out --- a/en/examples/bisect.commits.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.help.out --- a/en/examples/bisect.help.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.init.out --- a/en/examples/bisect.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.bad-init.out --- a/en/examples/bisect.search.bad-init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.good-init.out --- a/en/examples/bisect.search.good-init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.init.out --- a/en/examples/bisect.search.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.mytest.out --- a/en/examples/bisect.search.mytest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.reset.out --- a/en/examples/bisect.search.reset.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.rest.out --- a/en/examples/bisect.search.rest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.step1.out --- a/en/examples/bisect.search.step1.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/bisect.search.step2.out --- a/en/examples/bisect.search.step2.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named --- a/en/examples/branch-named Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/branch-named Tue Apr 21 00:36:40 2009 +0900 @@ -64,11 +64,10 @@ #$ name: update-bar hg update bar -hg update -C bar #$ name: merge hg branch -hg merge +hg merge foo hg commit -m 'Merge' hg tip diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.branch.out --- a/en/examples/branch-named.branch.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg branch} -default diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.branches.out --- a/en/examples/branch-named.branches.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \textbf{hg tip} -changeset: -tag: tip -user: Bryan O'Sullivan - -summary: Initial commit - -$ \textbf{hg branches} -default diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.commit.out --- a/en/examples/branch-named.commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.create.out --- a/en/examples/branch-named.create.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg branch foo} -marked working directory as branch foo -$ \textbf{hg branch} -foo diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.foo-commit.out --- a/en/examples/branch-named.foo-commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.merge.out --- a/en/examples/branch-named.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.parents.out --- a/en/examples/branch-named.parents.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.rebranch.out --- a/en/examples/branch-named.rebranch.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.status.out --- a/en/examples/branch-named.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \textbf{hg status} -$ \textbf{hg tip} -changeset: -tag: tip -user: Bryan O'Sullivan - -summary: Initial commit - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.update-bar.out --- a/en/examples/branch-named.update-bar.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.update-foo.out --- a/en/examples/branch-named.update-foo.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.update-nothing.out --- a/en/examples/branch-named.update-nothing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.update-switchy.out --- a/en/examples/branch-named.update-switchy.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-named.update.out --- a/en/examples/branch-named.update.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.bugfix.out --- a/en/examples/branch-repo.bugfix.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.clone.out --- a/en/examples/branch-repo.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ..} -$ \textbf{hg clone myproject myproject-1.0.1} -2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.merge.out --- a/en/examples/branch-repo.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.new.out --- a/en/examples/branch-repo.new.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.pull.out --- a/en/examples/branch-repo.pull.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branch-repo.tag.out --- a/en/examples/branch-repo.tag.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cd myproject} -$ \textbf{hg tag v1.0} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/branching.clone.out --- a/en/examples/branching.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.init.out --- a/en/examples/branching.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.main.out --- a/en/examples/branching.main.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.merge.out --- a/en/examples/branching.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.stable.out --- a/en/examples/branching.stable.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.tag.out --- a/en/examples/branching.tag.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/branching.update.out --- a/en/examples/branching.update.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/ch04/resolve --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/ch04/resolve Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,38 @@ +#$ name: init +hg init conflict +cd conflict +echo first > myfile.txt +hg ci -A -m first +cd .. +hg clone conflict left +hg clone conflict right + +#$ name: left +cd left +echo left >> myfile.txt +hg ci -m left + +#$ name: right +cd ../right +echo right >> myfile.txt +hg ci -m right + +#$ name: pull +cd ../conflict +hg pull -u ../left +hg pull -u ../right + +#$ name: heads +hg heads + +#$ name: export +export HGMERGE=merge + +#$ name: merge +hg merge + +#$ name: cifail +hg commit -m 'Attempt to commit a failed merge' + +#$ name: list +hg resolve -l diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/ch06/apache-config.lst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/ch06/apache-config.lst Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,11 @@ + + AllowOverride FileInfo AuthConfig Limit + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + + Order allow,deny + Allow from all + + + Order deny,allow Deny from all + + diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/ch10/bugzilla-config.lst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/ch10/bugzilla-config.lst Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,14 @@ +[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.\n + For complete details, see + {hgweb}{webroot}?cmd=changeset;node={node|short}\n + Changeset description:\n + \t{desc|tabindent} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/ch10/notify-config-mail.lst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/ch10/notify-config-mail.lst Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,18 @@ +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...] diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/ch10/notify-config.lst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/ch10/notify-config.lst Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,19 @@ +[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}\n + Subject: {webroot}: {desc|firstline|strip}\n + From: {author} + \n\n + changeset {node|short} in {root} + \n\ndetails: + {baseurl}{webroot}?cmd=changeset;node={node|short} + description: {desc|tabindent|strip} + +[web] +baseurl = +http://hg.example.com/ diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/cmdref.diff-p.out --- a/en/examples/cmdref.diff-p.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.after.out --- a/en/examples/daily.copy.after.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cp a z} -$ \textbf{hg copy --after a z} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.cat.out --- a/en/examples/daily.copy.cat.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{cat file} -line -new contents -$ \textbf{cat ../my-copy/new-file} -line diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.clone.out --- a/en/examples/daily.copy.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ..} -$ \textbf{hg clone my-copy your-copy} -1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.copy.out --- a/en/examples/daily.copy.copy.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cd my-copy} -$ \textbf{hg copy file new-file} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.dir-dest.out --- a/en/examples/daily.copy.dir-dest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{mkdir d} -$ \textbf{hg copy a b d} -$ \textbf{ls d} -a b diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.dir-src-dest.out --- a/en/examples/daily.copy.dir-src-dest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg copy c d} -copying c/a/c to d/c/a/c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.dir-src.out --- a/en/examples/daily.copy.dir-src.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg copy c e} -copying c/a/c to e/a/c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.init.out --- a/en/examples/daily.copy.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.merge.out --- a/en/examples/daily.copy.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.other.out --- a/en/examples/daily.copy.other.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ../your-copy} -$ \textbf{echo 'new contents' >> file} -$ \textbf{hg commit -m 'Changed file'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.simple.out --- a/en/examples/daily.copy.simple.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{mkdir k} -$ \textbf{hg copy a k} -$ \textbf{ls k} -a diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.status-copy.out --- a/en/examples/daily.copy.status-copy.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg status -C} -A new-file - file -$ \textbf{hg commit -m 'Copied file'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.copy.status.out --- a/en/examples/daily.copy.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg status} -A new-file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files --- a/en/examples/daily.files Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/daily.files Tue Apr 21 00:36:40 2009 +0900 @@ -4,9 +4,9 @@ hg init add-example cd add-example -echo a > a +echo a > myfile.txt hg status -hg add a +hg add myfile.txt hg status hg commit -m 'Added one file' hg status @@ -14,10 +14,10 @@ #$ name: add-dir mkdir b -echo b > b/b -echo c > b/c +echo b > b/somefile.txt +echo c > b/source.cpp mkdir b/d -echo d > b/d/d +echo d > b/d/test.h hg add b hg commit -m 'Added all files in subdirectory' diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.add-dir.out --- a/en/examples/daily.files.add-dir.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.add.out --- a/en/examples/daily.files.add.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.addremove.out --- a/en/examples/daily.files.addremove.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.commit-addremove.out --- a/en/examples/daily.files.commit-addremove.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{echo c > c} -$ \textbf{hg commit -A -m 'Commit with addremove'} -adding c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.hidden.out --- a/en/examples/daily.files.hidden.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.missing.out --- a/en/examples/daily.files.missing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.recover-missing.out --- a/en/examples/daily.files.recover-missing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg revert a} -$ \textbf{cat a} -a -$ \textbf{hg status} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.remove-after.out --- a/en/examples/daily.files.remove-after.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg remove --after a} -$ \textbf{hg status} -R a diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.files.remove.out --- a/en/examples/daily.files.remove.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.rename.rename.out --- a/en/examples/daily.rename.rename.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg rename a b} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.rename.status-copy.out --- a/en/examples/daily.rename.status-copy.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg status -C} -A b - a -R a diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.rename.status.out --- a/en/examples/daily.rename.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg status} -A b -R a diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.add.out --- a/en/examples/daily.revert.add.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \textbf{echo oops > oops} -$ \textbf{hg add oops} -$ \textbf{hg status oops} -A oops -$ \textbf{hg revert oops} -$ \textbf{hg status} -? oops diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.copy.out --- a/en/examples/daily.revert.copy.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg copy file new-file} -$ \textbf{hg revert new-file} -$ \textbf{hg status} -? new-file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.missing.out --- a/en/examples/daily.revert.missing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \textbf{rm file} -$ \textbf{hg status} -! file -$ \textbf{hg revert file} -$ \textbf{ls file} -file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.modify.out --- a/en/examples/daily.revert.modify.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.remove.out --- a/en/examples/daily.revert.remove.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \textbf{hg remove file} -$ \textbf{hg status} -R file -$ \textbf{hg revert file} -$ \textbf{hg status} -$ \textbf{ls file} -file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.rename-orig.out --- a/en/examples/daily.revert.rename-orig.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg revert file} -no changes needed to file -$ \textbf{hg status} -? new-file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.rename.out --- a/en/examples/daily.revert.rename.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg rename file new-file} -$ \textbf{hg revert new-file} -$ \textbf{hg status} -? new-file diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.status.out --- a/en/examples/daily.revert.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{hg status} -? file.orig -$ \textbf{cat file.orig} -original content -unwanted change diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/daily.revert.unmodify.out --- a/en/examples/daily.revert.unmodify.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{hg status} -M file -$ \textbf{hg revert file} -$ \textbf{cat file} -original content diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/extdiff.diff.out --- a/en/examples/extdiff.diff.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \textbf{hg diff} -diff -r myfile - - -@@ -1,1 +1,2 @@ The first line. - The first line. -+The second line. diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/extdiff.extdiff-ctx.out --- a/en/examples/extdiff.extdiff-ctx.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \textbf{hg extdiff -o -NprcC5} - - -*************** -*** 1 **** - - The first line. -+ The second line. diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/extdiff.extdiff.out --- a/en/examples/extdiff.extdiff.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \textbf{hg extdiff} - - -@@ -1 +1,2 @@ - The first line. -+The second line. diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.dirs.out --- a/en/examples/filenames.dirs.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{hg status src} -? src/main.py -? src/watcher/_watcher.c -? src/watcher/watcher.py -? src/xyzzy.txt diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.files.out --- a/en/examples/filenames.files.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg add COPYING README examples/simple.py} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.filter.exclude.out --- a/en/examples/filenames.filter.exclude.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg status -X '**.py' src} -? src/watcher/_watcher.c -? src/xyzzy.txt diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.filter.include.out --- a/en/examples/filenames.filter.include.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg status -I '*.in'} -? MANIFEST.in diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.group.out --- a/en/examples/filenames.glob.group.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg status 'glob:*.\{in,py\}'} -? MANIFEST.in -? setup.py diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.question.out --- a/en/examples/filenames.glob.question.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg status 'glob:**.?'} -? src/watcher/_watcher.c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.range.out --- a/en/examples/filenames.glob.range.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg status 'glob:**[nr-t]'} -? MANIFEST.in -? src/xyzzy.txt diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.star-starstar.out --- a/en/examples/filenames.glob.star-starstar.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.star.out --- a/en/examples/filenames.glob.star.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg add 'glob:*.py'} -adding main.py diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.glob.starstar.out --- a/en/examples/filenames.glob.starstar.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.wdir-relname.out --- a/en/examples/filenames.wdir-relname.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/filenames.wdir-subdir.out --- a/en/examples/filenames.wdir-subdir.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.msglen.go.out --- a/en/examples/hook.msglen.go.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.msglen.run.out --- a/en/examples/hook.msglen.run.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.simple.ext.out --- a/en/examples/hook.simple.ext.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.simple.init.out --- a/en/examples/hook.simple.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.simple.pretxncommit.out --- a/en/examples/hook.simple.pretxncommit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.ws.better.out --- a/en/examples/hook.ws.better.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/hook.ws.simple.out --- a/en/examples/hook.ws.simple.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/issue29.go.out --- a/en/examples/issue29.go.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.dodiff.diff.out --- a/en/examples/mq.dodiff.diff.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.init.out --- a/en/examples/mq.guards.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qguard.neg.out --- a/en/examples/mq.guards.qguard.neg.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg qguard hello.patch -quux} -$ \textbf{hg qguard hello.patch} -hello.patch: -quux diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qguard.out --- a/en/examples/mq.guards.qguard.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg qguard} -goodbye.patch: unguarded diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qguard.pos.out --- a/en/examples/mq.guards.qguard.pos.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg qguard +foo} -$ \textbf{hg qguard} -goodbye.patch: +foo diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.cat.out --- a/en/examples/mq.guards.qselect.cat.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cat .hg/patches/guards} -foo diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.error.out --- a/en/examples/mq.guards.qselect.error.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg qselect +foo} -abort: guard '+foo' starts with invalid character: '+' diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.foo.out --- a/en/examples/mq.guards.qselect.foo.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.foobar.out --- a/en/examples/mq.guards.qselect.foobar.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.qpush.out --- a/en/examples/mq.guards.qselect.qpush.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg qpush -a} -applying hello.patch -applying goodbye.patch -Now at: goodbye.patch diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.qselect.quux.out --- a/en/examples/mq.guards.qselect.quux.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.guards.series.out --- a/en/examples/mq.guards.series.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cat .hg/patches/series} -hello.patch #-quux -goodbye.patch #+foo diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.id.out.out --- a/en/examples/mq.id.out.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.id.output.out --- a/en/examples/mq.id.output.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.qinit-help.help.out --- a/en/examples/mq.qinit-help.help.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tarball.download.out --- a/en/examples/mq.tarball.download.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tarball.newsource.out --- a/en/examples/mq.tarball.newsource.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tarball.qinit.out --- a/en/examples/mq.tarball.qinit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tarball.repush.out --- a/en/examples/mq.tarball.repush.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tools.lsdiff.out --- a/en/examples/mq.tools.lsdiff.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tools.tools.out --- a/en/examples/mq.tools.tools.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.add.out --- a/en/examples/mq.tutorial.add.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qinit.out --- a/en/examples/mq.tutorial.qinit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qnew.out --- a/en/examples/mq.tutorial.qnew.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qnew2.out --- a/en/examples/mq.tutorial.qnew2.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qpop.out --- a/en/examples/mq.tutorial.qpop.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qpush-a.out --- a/en/examples/mq.tutorial.qpush-a.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \textbf{hg qpush -a} -applying second.patch -Now at: second.patch -$ \textbf{cat file1} -line 1 -line 2 -line 3 -line 4 diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qrefresh.out --- a/en/examples/mq.tutorial.qrefresh.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qrefresh2.out --- a/en/examples/mq.tutorial.qrefresh2.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/mq.tutorial.qseries.out --- a/en/examples/mq.tutorial.qseries.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \textbf{hg qseries} -first.patch -second.patch -$ \textbf{hg qapplied} -first.patch -second.patch diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rename.divergent --- a/en/examples/rename.divergent Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/rename.divergent Tue Apr 21 00:36:40 2009 +0900 @@ -14,7 +14,7 @@ #$ name: rename.anne cd anne -hg mv foo bar +hg rename foo bar hg ci -m 'Rename foo to bar' #$ name: rename.bob diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rename.divergent.clone.out --- a/en/examples/rename.divergent.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/rename.divergent.merge.out --- a/en/examples/rename.divergent.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# 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 5981a0f7540a -r 019040fbf5f5 en/examples/rename.divergent.rename.anne.out --- a/en/examples/rename.divergent.rename.anne.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd anne} -$ \textbf{hg mv foo bar} -$ \textbf{hg ci -m 'Rename foo to bar'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rename.divergent.rename.bob.out --- a/en/examples/rename.divergent.rename.bob.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ../bob} -$ \textbf{hg mv foo quux} -$ \textbf{hg ci -m 'Rename foo to quux'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.add.out --- a/en/examples/rollback.add.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg add b} -$ \textbf{hg commit -m 'Add file b, this time for real'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.commit.out --- a/en/examples/rollback.commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg status} -M a -$ \textbf{echo b > b} -$ \textbf{hg commit -m 'Add file b'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.rollback.out --- a/en/examples/rollback.rollback.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.status.out --- a/en/examples/rollback.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \textbf{hg status} -? b -$ \textbf{hg tip} -changeset: -tag: tip -user: Bryan O'Sullivan - -summary: Add file b - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.tip.out diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/rollback.twice.out --- a/en/examples/rollback.twice.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg rollback} -rolling back last transaction -$ \textbf{hg rollback} -no rollback information available diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/run-example --- a/en/examples/run-example Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/run-example Tue Apr 21 00:36:40 2009 +0900 @@ -7,6 +7,7 @@ import cStringIO import errno import getopt +import glob import os import pty import re @@ -18,23 +19,23 @@ import tempfile import time -tex_subs = { - '\\': '\\textbackslash{}', - '{': '\\{', - '}': '\\}', +xml_subs = { + '<': '<', + '>': '>', + '&': '&', } def gensubs(s): start = 0 for i, c in enumerate(s): - sub = tex_subs.get(c) + sub = xml_subs.get(c) if sub: yield s[start:i] start = i + 1 yield sub yield s[start:] -def tex_escape(s): +def xml_escape(s): return ''.join(gensubs(s)) def maybe_unlink(name): @@ -53,7 +54,92 @@ return p return None +def result_name(name): + return os.path.normpath(os.path.join('results', name.replace(os.sep, '-'))) + +def wopen(name): + path = os.path.dirname(name) + if path: + try: + os.makedirs(path) + except OSError, err: + if err.errno != errno.EEXIST: + raise + return open(name, 'w') + class example: + entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml')) + + def __init__(self, name, verbose, keep_change): + self.name = os.path.normpath(name) + self.verbose = verbose + self.keep_change = keep_change + + def status(self, s): + sys.stdout.write(s) + if not s.endswith('\n'): + sys.stdout.flush() + + 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 = result_name(base + '.out') + tmpname = result_name(base + '.tmp') + errname = result_name(base + '.err') + errfp = open(errname, 'w+') + for line in open(tmpname): + errfp.write(mangle_re.sub('', line)) + os.rename(tmpname, result_name(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 + if self.keep_change: + os.rename(errname, oldname) + return False + else: + os.system('diff -u %s %s 1>&2' % (oldname, errname)) + return True + +class static_example(example): + def run(self): + self.status('running %s\n' % self.name) + s = open(self.name).read().rstrip() + s = s.replace('&', '&').replace('<', '<').replace('>', '>') + ofp = wopen(result_name(self.name + '.tmp')) + ofp.write('\n' % self.name) + ofp.write('') + ofp.write(s) + ofp.write('\n') + ofp.write('\n' % self.name) + ofp.close() + self.rename_output(self.name) + norm = self.name.replace(os.sep, '-') + example.entities[ + '' % (norm, norm)] = 1 + + +class shell_example(example): shell = '/usr/bin/env bash' ps1 = '__run_example_ps1__ ' ps2 = '__run_example_ps2__ ' @@ -61,9 +147,8 @@ timeout = 10 - def __init__(self, name, verbose): - self.name = name - self.verbose = verbose + def __init__(self, name, verbose, keep_change): + example.__init__(self, name, verbose, keep_change) self.poll = select.poll() def parse(self): @@ -76,11 +161,6 @@ 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: @@ -146,12 +226,12 @@ maybe_unlink(self.name + '.run') rcfile = os.path.join(tmpdir, '.hgrc') - rcfp = open(rcfile, 'w') + rcfp = wopen(rcfile) print >> rcfp, '[ui]' print >> rcfp, "username = Bryan O'Sullivan " rcfile = os.path.join(tmpdir, '.bashrc') - rcfp = open(rcfile, 'w') + rcfp = wopen(rcfile) print >> rcfp, 'PS1="%s"' % self.ps1 print >> rcfp, 'PS2="%s"' % self.ps2 print >> rcfp, 'unset HISTFILE' @@ -230,12 +310,22 @@ return 1 assert os.sep not in out if ofp is not None: + ofp.write('\n') + ofp.write('\n' % ofp_basename) ofp.close() err |= self.rename_output(ofp_basename, ignore) if out: ofp_basename = '%s.%s' % (self.name, out) + norm = os.path.normpath(ofp_basename) + norm = norm.replace(os.sep, '-') + example.entities[ + '' + % (norm, norm)] = 1 read_hint = ofp_basename + ' ' - ofp = open(ofp_basename + '.tmp', 'w') + ofp = wopen(result_name(ofp_basename + '.tmp')) + ofp.write('\n' % ofp_basename) + ofp.write('') else: ofp = None elif pi == 'ignore': @@ -248,14 +338,15 @@ # first, print the command we ran if not hunk.startswith('#'): nl = hunk.endswith('\n') - hunk = ('%s \\textbf{%s}' % + hunk = ('%s ' + '%s' % (prompts[ps], - tex_escape(hunk.rstrip('\n')))) + xml_escape(hunk.rstrip('\n')))) if nl: hunk += '\n' ofp.write(hunk) # then its output - ofp.write(tex_escape(output)) - ps = newps + ofp.write(xml_escape(output)) + ps = newps self.status('\n') except: print >> sys.stderr, '(killed)' @@ -267,6 +358,8 @@ ps, output = self.sendreceive('exit\n', read_hint) if ofp is not None: ofp.write(output) + ofp.write('\n') + ofp.write('\n' % ofp_basename) ofp.close() err |= self.rename_output(ofp_basename, ignore) os.close(self.cfd) @@ -281,69 +374,40 @@ elif os.WIFSIGNALED(rc): print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc) else: - open(self.name + '.run', 'w') -# return err - return 0 + wopen(result_name(self.name + '.run')) + 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, ' -a --all run all examples in this directory' print >> sys.stderr, ' -h --help print this help message' + print >> sys.stderr, ' --keep keep new output as desired output' print >> sys.stderr, ' -v --verbose display extra debug output' sys.exit(exit) def main(path='.'): + if os.path.realpath(path).split(os.sep)[-1] != 'examples': + print >> sys.stderr, 'Not being run from the examples directory!' + sys.exit(1) + opts, args = getopt.getopt(sys.argv[1:], '?ahv', - ['all', 'help', 'verbose']) + ['all', 'help', 'keep', 'verbose']) verbose = False run_all = False + keep_change = 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 ('--keep',): + keep_change = True if o in ('-v', '--verbose'): verbose = True errs = 0 @@ -355,19 +419,20 @@ 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 + if stat.S_ISREG(st.st_mode): + if st.st_mode & 0111: + if shell_example(a, verbose, keep_change).run(): + errs += 1 + elif a.endswith('.lst'): + static_example(a, verbose, keep_change).run() else: print >> sys.stderr, '%s: not a file, or not executable' % a errs += 1 elif run_all: - names = os.listdir(path) + names = glob.glob("*") + glob.glob("app*/*") + glob.glob("ch*/*") 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 + if name == 'run-example' or name.endswith('~'): continue pathname = os.path.join(path, name) try: st = os.lstat(pathname) @@ -376,12 +441,20 @@ 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() + if stat.S_ISREG(st.st_mode): + if st.st_mode & 0111: + if shell_example(pathname, verbose, keep_change).run(): + errs += 1 + elif pathname.endswith('.lst'): + static_example(pathname, verbose, keep_change).run() + print >> wopen(os.path.join(path, '.run')), time.asctime() else: print_help(1, msg='no test names given, and --all not provided') + + fp = wopen('auto-snippets.xml') + for key in sorted(example.entities.iterkeys()): + print >> fp, key + fp.close() return errs if __name__ == '__main__': diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tag.init.out --- a/en/examples/tag.init.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{hg init mytag} -$ \textbf{cd mytag} -$ \textbf{echo hello > myfile} -$ \textbf{hg commit -A -m 'Initial commit'} -adding myfile diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tag.log.out --- a/en/examples/tag.log.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tag.log.v1.0.out --- a/en/examples/tag.log.v1.0.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tag.remove.out --- a/en/examples/tag.remove.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg tag --remove v1.0} -$ \textbf{hg tags} -tip diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tag.replace.out --- a/en/examples/tag.replace.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tag.tag.out --- a/en/examples/tag.tag.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg tag v1.0} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tag.tags.out --- a/en/examples/tag.tags.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{hg tags} -tip -v1.0 diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tag.tip.out --- a/en/examples/tag.tip.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \textbf{hg tip} -changeset: -tag: tip -user: Bryan O'Sullivan - -summary: Added tag v1.1 for changeset - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.changelog.out --- a/en/examples/template.simple.changelog.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.combine.out --- a/en/examples/template.simple.combine.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.compact.out --- a/en/examples/template.simple.compact.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.datekeyword.out --- a/en/examples/template.simple.datekeyword.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.keywords.out --- a/en/examples/template.simple.keywords.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.manyfilters.out --- a/en/examples/template.simple.manyfilters.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.normal.out --- a/en/examples/template.simple.normal.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.rev.out --- a/en/examples/template.simple.rev.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.simplest.out --- a/en/examples/template.simple.simplest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.simple.simplesub.out --- a/en/examples/template.simple.simplesub.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ - - - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle --- a/en/examples/template.svnstyle Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/template.svnstyle Tue Apr 21 00:36:40 2009 +0900 @@ -34,6 +34,7 @@ hg log -r0 --template '{node}' #$ name: simplest +#$ ignore: \d+-\d+-\d+ \d+:\d+ \+.* cat svn.style hg log -r1 --style svn.style diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.id.out --- a/en/examples/template.svnstyle.id.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg log -r0 --template '\{node\}'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.result.out --- a/en/examples/template.svnstyle.result.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.short.out --- a/en/examples/template.svnstyle.short.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.simplest.out --- a/en/examples/template.svnstyle.simplest.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{cat svn.style} -changeset = "\{node|short\}\textbackslash{}n" -$ \textbf{hg log -r1 --style svn.style} - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.style.out --- a/en/examples/template.svnstyle.style.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cat svn.style} -header = '------------------------------------------------------------------------\textbackslash{}n\textbackslash{}n' -changeset = svn.template diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.syntax.error.out --- a/en/examples/template.svnstyle.syntax.error.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg log -r1 --style broken.style} -abort: broken.style:1: parse error diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.syntax.input.out --- a/en/examples/template.svnstyle.syntax.input.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{cat broken.style} -changeset = diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/template.svnstyle.template.out --- a/en/examples/template.svnstyle.template.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \textbf{cat svn.template} -r\{rev\} | \{author|user\} | \{date|isodate\} (\{date|rfc822date\}) - -\{desc|strip|fill76\} - ------------------------------------------------------------------------- diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour --- a/en/examples/tour Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/tour Tue Apr 21 00:36:40 2009 +0900 @@ -31,7 +31,7 @@ #$ name: log-r hg log -r 3 -hg log -r ff5d7b70a2a9 +hg log -r 0272e0d5a517 hg log -r 1 -r 4 #$ name: log.range @@ -52,10 +52,17 @@ hg clone hello my-hello cd my-hello -#$ name: sed +#$ name: cat1 +cat hello.c + +#$ name: sed -i '/printf/a\\tprintf("hello again!\\n");' hello.c +#$ name: cat2 +# ... edit edit edit ... +cat hello.c + #$ name: status ls @@ -73,6 +80,10 @@ hg commit +#$ name: merge.dummy1 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-hello + #$ name: tip hg tip -vp @@ -135,14 +146,23 @@ hg push http://hg.serpentine.com/tutorial/hello +#$ name: +cp hello.c ../new-hello.c +sed -i '/printf/i\\tprintf("once more, hello.\\n");' ../new-hello.c + #$ 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 +# The file new-hello.c is lightly edited. +cp ../new-hello.c hello.c hg commit -m 'A new hello for a new day.' +#$ name: merge.dummy2 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-new-hello + #$ name: merge.cat cat hello.c @@ -152,6 +172,10 @@ hg pull ../my-hello +#$ name: merge.dummy3 + +hg log -r 6 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV6.my-new-hello + #$ name: merge.heads hg heads @@ -173,6 +197,10 @@ hg commit -m 'Merged changes' +#$ name: merge.dummy4 + +hg log -r 7 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV7.my-new-hello + #$ name: merge.tip hg tip diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict --- a/en/examples/tour-merge-conflict Mon Apr 20 23:50:34 2009 +0900 +++ b/en/examples/tour-merge-conflict Tue Apr 21 00:36:40 2009 +0900 @@ -68,5 +68,6 @@ Nigerian dictator Sani Abacha. EOF +hg resolve -m letter.txt hg commit -m 'Send me your money' hg tip diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.commit.out --- a/en/examples/tour-merge-conflict.commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -$ \textbf{cat > 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 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.cousin.out --- a/en/examples/tour-merge-conflict.cousin.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.merge.out --- a/en/examples/tour-merge-conflict.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.pull.out --- a/en/examples/tour-merge-conflict.pull.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.son.out --- a/en/examples/tour-merge-conflict.son.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour-merge-conflict.wife.out --- a/en/examples/tour-merge-conflict.wife.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.clone-pull.out --- a/en/examples/tour.clone-pull.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ..} -$ \textbf{hg clone hello hello-pull} -2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.clone-push.out --- a/en/examples/tour.clone-push.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd ..} -$ \textbf{hg clone hello hello-push} -2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.clone.out --- a/en/examples/tour.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.commit.out --- a/en/examples/tour.commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg commit} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.diff.out --- a/en/examples/tour.diff.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.help.out --- a/en/examples/tour.help.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.incoming.out --- a/en/examples/tour.incoming.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.log-r.out --- a/en/examples/tour.log-r.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.log-v.out --- a/en/examples/tour.log-v.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.log-vp.out --- a/en/examples/tour.log-vp.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.log.out --- a/en/examples/tour.log.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.log.range.out --- a/en/examples/tour.log.range.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.ls-a.out --- a/en/examples/tour.ls-a.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -$ \textbf{cd hello} -$ \textbf{ls -a} -. .. .hg Makefile hello.c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.ls.out --- a/en/examples/tour.ls.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -$ \textbf{ls -l} -total 4 - -$ \textbf{ls hello} -Makefile hello.c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.cat.out --- a/en/examples/tour.merge.cat.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.clone.out --- a/en/examples/tour.merge.clone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.commit.out --- a/en/examples/tour.merge.commit.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{hg commit -m 'Merged changes'} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.heads.out --- a/en/examples/tour.merge.heads.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.merge.out --- a/en/examples/tour.merge.merge.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.parents.out --- a/en/examples/tour.merge.parents.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.pull.out --- a/en/examples/tour.merge.pull.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.tip.out --- a/en/examples/tour.merge.tip.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \textbf{hg tip} -changeset: -tag: tip -parent: -parent: -user: Bryan O'Sullivan - -summary: Merged changes - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.merge.update.out --- a/en/examples/tour.merge.update.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -$ \textbf{hg update} -abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.older.out --- a/en/examples/tour.older.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.outgoing.net.out --- a/en/examples/tour.outgoing.net.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.outgoing.out --- a/en/examples/tour.outgoing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.parents.out --- a/en/examples/tour.parents.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \textbf{hg parents} -changeset: -tag: tip -user: Bryan O'Sullivan - -summary: Added an extra line of output - diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.pull.out --- a/en/examples/tour.pull.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.push.net.out --- a/en/examples/tour.push.net.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg push http://hg.serpentine.com/tutorial/hello} -pushing to http://hg.serpentine.com/tutorial/hello -searching for changes -ssl required diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.push.nothing.out --- a/en/examples/tour.push.nothing.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{hg push ../hello-push} -pushing to ../hello-push -searching for changes -no changes found diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.push.out --- a/en/examples/tour.push.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.reclone.out --- a/en/examples/tour.reclone.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.sed.out --- a/en/examples/tour.sed.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -$ \textbf{sed -i '/printf/a\textbackslash{}\textbackslash{}tprintf("hello again!\textbackslash{}\textbackslash{}n");' hello.c} diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.status.out --- a/en/examples/tour.status.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -$ \textbf{ls} -Makefile hello.c -$ \textbf{hg status} -M hello.c diff -r 5981a0f7540a -r 019040fbf5f5 en/examples/tour.tip.out --- a/en/examples/tour.tip.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.update.out --- a/en/examples/tour.update.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/examples/tour.version.out --- a/en/examples/tour.version.out Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -$ \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 5981a0f7540a -r 019040fbf5f5 en/feature-branches.dot --- a/en/feature-branches.dot Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -digraph feature_branches { - master -> crypto; - master -> filesystems; - master -> ipc; - master -> memory; - master -> network; - master -> security; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/feature-branches.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/feature-branches.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +digraph feature_branches { + master -> crypto; + master -> filesystems; + master -> ipc; + master -> memory; + master -> network; + master -> security; +} diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/filelog.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + .hg/store/data/_r_e_a_d_m_e.i + + + + + README + + + + + + + + + .hg/store/data/src/hello.c.d + .hg/store/data/src/hello.c.i + + + + + src/hello.c + + + + Working directory + Repository + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/kdiff3.png Binary file en/figs/kdiff3.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/metadata.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Changelog + Manifest + Filelogs + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/mq-stack.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/note.png Binary file en/figs/note.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/revlog.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/snapshot.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/throbber.gif Binary file en/figs/throbber.gif has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/tip.png Binary file en/figs/tip.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/tour-history.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + (newest) + (oldest) + + 4: REV4 + + + revisionnumber + changesetidentifier + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/tour-merge-conflict.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/tour-merge-merge.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip (and head) + head + + + + merge + working directoryduring merge + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip + + + 7: REV7_my_new_hello + Working directory during merge + Repository after merge committed + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/tour-merge-pull.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip (and head) + head + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/tour-merge-sep-repos.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,466 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_hello + + my-hello + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + my-new-hello + newest changesdiffer + common history + + + + + + + head revision(has no children) + + diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/undo-manual-merge.dot Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/undo-manual.dot Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/undo-non-tip.dot Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/undo-simple.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,4 @@ +digraph undo_simple { + "first change" -> "second change"; + "second change" -> "back out\nsecond change"; +} diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/warning.png Binary file en/figs/warning.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/figs/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/wdir-after-commit.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/wdir-branch.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/wdir-merge.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/wdir-pre-branch.svg Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 en/figs/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/figs/wdir.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + History in repository + + + + + e7639888bb2f + + + + 000000000000 + + First parent + Second parent + + Parents of working directory + + + + diff -r 5981a0f7540a -r 019040fbf5f5 en/filelog.svg --- a/en/filelog.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,373 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - .hg/store/data/README.i - - - - - README - - - - - - - - - .hg/store/data/src/hello.c.d - .hg/store/data/src/hello.c.i - - - - - src/hello.c - - - - Working directory - Repository - - diff -r 5981a0f7540a -r 019040fbf5f5 en/filenames.tex --- a/en/filenames.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,306 +0,0 @@ -\chapter{File names and pattern matching} -\label{chap:names} - -Mercurial provides mechanisms that let you work with file names in a -consistent and expressive way. - -\section{Simple file naming} - -Mercurial uses a unified piece of machinery ``under the hood'' to -handle file names. Every command behaves uniformly with respect to -file names. The way in which commands work with file names is as -follows. - -If you explicitly name real files on the command line, Mercurial works -with exactly those files, as you would expect. -\interaction{filenames.files} - -When you provide a directory name, Mercurial will interpret this as -``operate on every file in this directory and its subdirectories''. -Mercurial traverses the files and subdirectories in a directory in -alphabetical order. When it encounters a subdirectory, it will -traverse that subdirectory before continuing with the current -directory. -\interaction{filenames.dirs} - -\section{Running commands without any file names} - -Mercurial's commands that work with file names have useful default -behaviours when you invoke them without providing any file names or -patterns. What kind of behaviour you should expect depends on what -the command does. Here are a few rules of thumb you can use to -predict what a command is likely to do if you don't give it any names -to work with. -\begin{itemize} -\item Most commands will operate on the entire working directory. - This is what the \hgcmd{add} command does, for example. -\item If the command has effects that are difficult or impossible to - reverse, it will force you to explicitly provide at least one name - or pattern (see below). This protects you from accidentally - deleting files by running \hgcmd{remove} with no arguments, for - example. -\end{itemize} - -It's easy to work around these default behaviours if they don't suit -you. If a command normally operates on the whole working directory, -you can invoke it on just the current directory and its subdirectories -by giving it the name ``\dirname{.}''. -\interaction{filenames.wdir-subdir} - -Along the same lines, some commands normally print file names relative -to the root of the repository, even if you're invoking them from a -subdirectory. Such a command will print file names relative to your -subdirectory if you give it explicit names. Here, we're going to run -\hgcmd{status} from a subdirectory, and get it to operate on the -entire working directory while printing file names relative to our -subdirectory, by passing it the output of the \hgcmd{root} command. -\interaction{filenames.wdir-relname} - -\section{Telling you what's going on} - -The \hgcmd{add} example in the preceding section illustrates something -else that's helpful about Mercurial commands. If a command operates -on a file that you didn't name explicitly on the command line, it will -usually print the name of the file, so that you will not be surprised -what's going on. - -The principle here is of \emph{least surprise}. If you've exactly -named a file on the command line, there's no point in repeating it -back at you. If Mercurial is acting on a file \emph{implicitly}, -because you provided no names, or a directory, or a pattern (see -below), it's safest to tell you what it's doing. - -For commands that behave this way, you can silence them using the -\hggopt{-q} option. You can also get them to print the name of every -file, even those you've named explicitly, using the \hggopt{-v} -option. - -\section{Using patterns to identify files} - -In addition to working with file and directory names, Mercurial lets -you use \emph{patterns} to identify files. Mercurial's pattern -handling is expressive. - -On Unix-like systems (Linux, MacOS, etc.), the job of matching file -names to patterns normally falls to the shell. On these systems, you -must explicitly tell Mercurial that a name is a pattern. On Windows, -the shell does not expand patterns, so Mercurial will automatically -identify names that are patterns, and expand them for you. - -To provide a pattern in place of a regular name on the command line, -the mechanism is simple: -\begin{codesample2} - syntax:patternbody -\end{codesample2} -That is, a pattern is identified by a short text string that says what -kind of pattern this is, followed by a colon, followed by the actual -pattern. - -Mercurial supports two kinds of pattern syntax. The most frequently -used is called \texttt{glob}; this is the same kind of pattern -matching used by the Unix shell, and should be familiar to Windows -command prompt users, too. - -When Mercurial does automatic pattern matching on Windows, it uses -\texttt{glob} syntax. You can thus omit the ``\texttt{glob:}'' prefix -on Windows, but it's safe to use it, too. - -The \texttt{re} syntax is more powerful; it lets you specify patterns -using regular expressions, also known as regexps. - -By the way, in the examples that follow, notice that I'm careful to -wrap all of my patterns in quote characters, so that they won't get -expanded by the shell before Mercurial sees them. - -\subsection{Shell-style \texttt{glob} patterns} - -This is an overview of the kinds of patterns you can use when you're -matching on glob patterns. - -The ``\texttt{*}'' character matches any string, within a single -directory. -\interaction{filenames.glob.star} - -The ``\texttt{**}'' pattern matches any string, and crosses directory -boundaries. It's not a standard Unix glob token, but it's accepted by -several popular Unix shells, and is very useful. -\interaction{filenames.glob.starstar} - -The ``\texttt{?}'' pattern matches any single character. -\interaction{filenames.glob.question} - -The ``\texttt{[}'' character begins a \emph{character class}. This -matches any single character within the class. The class ends with a -``\texttt{]}'' character. A class may contain multiple \emph{range}s -of the form ``\texttt{a-f}'', which is shorthand for -``\texttt{abcdef}''. -\interaction{filenames.glob.range} -If the first character after the ``\texttt{[}'' in a character class -is a ``\texttt{!}'', it \emph{negates} the class, making it match any -single character not in the class. - -A ``\texttt{\{}'' begins a group of subpatterns, where the whole group -matches if any subpattern in the group matches. The ``\texttt{,}'' -character separates subpatterns, and ``\texttt{\}}'' ends the group. -\interaction{filenames.glob.group} - -\subsubsection{Watch out!} - -Don't forget that if you want to match a pattern in any directory, you -should not be using the ``\texttt{*}'' match-any token, as this will -only match within one directory. Instead, use the ``\texttt{**}'' -token. This small example illustrates the difference between the two. -\interaction{filenames.glob.star-starstar} - -\subsection{Regular expression matching with \texttt{re} patterns} - -Mercurial accepts the same regular expression syntax as the Python -programming language (it uses Python's regexp engine internally). -This is based on the Perl language's regexp syntax, which is the most -popular dialect in use (it's also used in Java, for example). - -I won't discuss Mercurial's regexp dialect in any detail here, as -regexps are not often used. Perl-style regexps are in any case -already exhaustively documented on a multitude of web sites, and in -many books. Instead, I will focus here on a few things you should -know if you find yourself needing to use regexps with Mercurial. - -A regexp is matched against an entire file name, relative to the root -of the repository. In other words, even if you're already in -subbdirectory \dirname{foo}, if you want to match files under this -directory, your pattern must start with ``\texttt{foo/}''. - -One thing to note, if you're familiar with Perl-style regexps, is that -Mercurial's are \emph{rooted}. That is, a regexp starts matching -against the beginning of a string; it doesn't look for a match -anywhere within the string. To match anywhere in a string, start -your pattern with ``\texttt{.*}''. - -\section{Filtering files} - -Not only does Mercurial give you a variety of ways to specify files; -it lets you further winnow those files using \emph{filters}. Commands -that work with file names accept two filtering options. -\begin{itemize} -\item \hggopt{-I}, or \hggopt{--include}, lets you specify a pattern - that file names must match in order to be processed. -\item \hggopt{-X}, or \hggopt{--exclude}, gives you a way to - \emph{avoid} processing files, if they match this pattern. -\end{itemize} -You can provide multiple \hggopt{-I} and \hggopt{-X} options on the -command line, and intermix them as you please. Mercurial interprets -the patterns you provide using glob syntax by default (but you can use -regexps if you need to). - -You can read a \hggopt{-I} filter as ``process only the files that -match this filter''. -\interaction{filenames.filter.include} -The \hggopt{-X} filter is best read as ``process only the files that -don't match this pattern''. -\interaction{filenames.filter.exclude} - -\section{Ignoring unwanted files and directories} - -XXX. - -\section{Case sensitivity} -\label{sec:names:case} - -If you're working in a mixed development environment that contains -both Linux (or other Unix) systems and Macs or Windows systems, you -should keep in the back of your mind the knowledge that they treat the -case (``N'' versus ``n'') of file names in incompatible ways. This is -not very likely to affect you, and it's easy to deal with if it does, -but it could surprise you if you don't know about it. - -Operating systems and filesystems differ in the way they handle the -\emph{case} of characters in file and directory names. There are -three common ways to handle case in names. -\begin{itemize} -\item Completely case insensitive. Uppercase and lowercase versions - of a letter are treated as identical, both when creating a file and - during subsequent accesses. This is common on older DOS-based - systems. -\item Case preserving, but insensitive. When a file or directory is - created, the case of its name is stored, and can be retrieved and - displayed by the operating system. When an existing file is being - looked up, its case is ignored. This is the standard arrangement on - Windows and MacOS. The names \filename{foo} and \filename{FoO} - identify the same file. This treatment of uppercase and lowercase - letters as interchangeable is also referred to as \emph{case - folding}. -\item Case sensitive. The case of a name is significant at all times. - The names \filename{foo} and {FoO} identify different files. This - is the way Linux and Unix systems normally work. -\end{itemize} - -On Unix-like systems, it is possible to have any or all of the above -ways of handling case in action at once. For example, if you use a -USB thumb drive formatted with a FAT32 filesystem on a Linux system, -Linux will handle names on that filesystem in a case preserving, but -insensitive, way. - -\subsection{Safe, portable repository storage} - -Mercurial's repository storage mechanism is \emph{case safe}. It -translates file names so that they can be safely stored on both case -sensitive and case insensitive filesystems. This means that you can -use normal file copying tools to transfer a Mercurial repository onto, -for example, a USB thumb drive, and safely move that drive and -repository back and forth between a Mac, a PC running Windows, and a -Linux box. - -\subsection{Detecting case conflicts} - -When operating in the working directory, Mercurial honours the naming -policy of the filesystem where the working directory is located. If -the filesystem is case preserving, but insensitive, Mercurial will -treat names that differ only in case as the same. - -An important aspect of this approach is that it is possible to commit -a changeset on a case sensitive (typically Linux or Unix) filesystem -that will cause trouble for users on case insensitive (usually Windows -and MacOS) users. If a Linux user commits changes to two files, one -named \filename{myfile.c} and the other named \filename{MyFile.C}, -they will be stored correctly in the repository. And in the working -directories of other Linux users, they will be correctly represented -as separate files. - -If a Windows or Mac user pulls this change, they will not initially -have a problem, because Mercurial's repository storage mechanism is -case safe. However, once they try to \hgcmd{update} the working -directory to that changeset, or \hgcmd{merge} with that changeset, -Mercurial will spot the conflict between the two file names that the -filesystem would treat as the same, and forbid the update or merge -from occurring. - -\subsection{Fixing a case conflict} - -If you are using Windows or a Mac in a mixed environment where some of -your collaborators are using Linux or Unix, and Mercurial reports a -case folding conflict when you try to \hgcmd{update} or \hgcmd{merge}, -the procedure to fix the problem is simple. - -Just find a nearby Linux or Unix box, clone the problem repository -onto it, and use Mercurial's \hgcmd{rename} command to change the -names of any offending files or directories so that they will no -longer cause case folding conflicts. Commit this change, \hgcmd{pull} -or \hgcmd{push} it across to your Windows or MacOS system, and -\hgcmd{update} to the revision with the non-conflicting names. - -The changeset with case-conflicting names will remain in your -project's history, and you still won't be able to \hgcmd{update} your -working directory to that changeset on a Windows or MacOS system, but -you can continue development unimpeded. - -\begin{note} - Prior to version~0.9.3, Mercurial did not use a case safe repository - storage mechanism, and did not detect case folding conflicts. If - you are using an older version of Mercurial on Windows or MacOS, I - strongly recommend that you upgrade. -\end{note} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/fixhtml.py --- a/en/fixhtml.py Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -# This script attempts to work around some of the more bizarre and -# quirky behaviours of htlatex. -# -# - We've persuaded htlatex to produce UTF-8, which unfortunately -# causes it to use huge character sequences to represent even the -# safe 7-bit ASCII subset of UTF-8. We fix that up. -# -# - BUT we have to treat angle brackets (for example, redirections in -# shell script snippets) specially, otherwise they'll break the -# generated HTML. (Reported by Johannes Hoff.) -# -# - For some reason, htlatex gives a unique ID to each fancyvrb -# environment, which makes writing a sane, small CSS stylesheet -# impossible. We squish all those IDs down to nothing. - -import os -import sys -import re - -angle_re = re.compile(r'([CE];)') -unicode_re = re.compile(r'�([0-7][0-9A-F]);') -fancyvrb_re = re.compile(r'id="fancyvrb\d+"', re.I) -ligature_re = re.compile(r'ྰ([0-4]);') - -tmpsuffix = '.tmp.' + str(os.getpid()) - -def hide_angle(m): - return m.group(1).lower() - -def fix_ascii(m): - return chr(int(m.group(1), 16)) - -ligatures = ['ff', 'fi', 'fl', 'ffi', 'ffl'] - -def expand_ligature(m): - return ligatures[int(m.group(1))] - -for name in sys.argv[1:]: - tmpname = name + tmpsuffix - ofp = file(tmpname, 'w') - for line in file(name): - line = angle_re.sub(hide_angle, line) - line = unicode_re.sub(fix_ascii, line) - line = ligature_re.sub(expand_ligature, line) - line = fancyvrb_re.sub('id="fancyvrb"', line) - ofp.write(line) - ofp.close() - os.rename(tmpname, name) diff -r 5981a0f7540a -r 019040fbf5f5 en/fixsvg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/fixsvg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,29 @@ +#!/bin/bash + +test -d hello || hg clone http://hg.serpentine.com/tutorial/hello + +set -e + +for i in 0 1 2 3 4 +do + export REV$i=$(hg --cwd hello log -r $i --template '{node|short}' | cut -c1-4) +done +export REV_my_hello=$(cat /tmp/REV5.my-hello) +export REV_my_new_hello=$(cat /tmp/REV5.my-new-hello) +export REV6_my_new_hello=$(cat /tmp/REV6.my-new-hello) +export REV7_my_new_hello=$(cat /tmp/REV7.my-new-hello) + +FILE=$1 +OUTFILE=$FILE-tmp.svg +rm -f $OUTFILE +echo "Fixing $FILE" +cp $FILE $OUTFILE +perl -p -i -e "s#REV0#$REV0#" $OUTFILE +perl -p -i -e "s#REV1#$REV1#" $OUTFILE +perl -p -i -e "s#REV2#$REV2#" $OUTFILE +perl -p -i -e "s#REV3#$REV3#" $OUTFILE +perl -p -i -e "s#REV4#$REV4#" $OUTFILE +perl -p -i -e "s#REV_my_hello#$REV_my_hello#" $OUTFILE +perl -p -i -e "s#REV_my_new_hello#$REV_my_new_hello#" $OUTFILE +perl -p -i -e "s#REV6_my_new_hello#$REV6_my_new_hello#" $OUTFILE +perl -p -i -e "s#REV7_my_new_hello#$REV7_my_new_hello#" $OUTFILE diff -r 5981a0f7540a -r 019040fbf5f5 en/hgbook.css --- a/en/hgbook.css Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -body { - font: 12px/1.5 Verdana, sans-serif; - padding-top: 50px; - padding-left: 80px; - padding-right: 80px; - padding-bottom: 90px; -} -.ptmr7t- { - font-family: monospace; -} -.ptmr7t-x-x-172 { - font-size: 172%; - font-family: monospace; -} -.ptmr7t-x-x-120 { - font-size: 120%; -} -.zpzccmry-x-x-120 { - font-size: 120%; - font-weight: bold; - font-style: italic; -} -.zpzccmry-x-x-120 { - font-weight: bold; - font-style: italic; -} -.pcrr7tn- { - font-family: monospace; -} -.ptmri7t- { - font-style: italic; -} -.ptmr7t-x-x-50 { - font-size: 50%; - font-family: monospace; -} -.ptmb7t- { - font-weight: bold; -} -.zptmcmr- { - font-style: italic; -} -.zptmcmrm- { - font-style: italic; -} -.zpzccmry- { - font-weight: bold; - font-style: italic; -} -.pcrb7t- { - font-family: monospace; - font-weight: bold; -} -.pcrro7t- { - font-family: monospace; - font-style: oblique; -} -p.noindent { - text-indent: 0em; - margin: 0em; -} -p.nopar { - text-indent: 0em; -} -p.indent { - text-indent: 1.5em; - margin: 0em; -} -a img { - border-top: 0; - border-left: 0; - border-right: 0; -} -center { - margin-top: 1em; - margin-bottom: 1em; -} -td center { - margin-top: 0em; - margin-bottom: 0em; -} -.Canvas { - position: relative; -} -img.math { - vertical-align: middle; -} -li p.indent { - text-indent: 0em; -} -.enumerate1 { - list-style-type: decimal; -} -.enumerate2 { - list-style-type: lower-alpha; -} -.enumerate3 { - list-style-type: lower-roman; -} -.enumerate4 { - list-style-type: upper-alpha; -} -div.newtheorem { - margin-bottom: 2em; - margin-top: 2em; -} -.obeylines-h,.obeylines-v { - white-space: nowrap; -} -div.obeylines-v p { - margin-top: 0; - margin-bottom: 0; -} -.overline { - text-decoration: overline; -} -.overline img { - border-top: 1px solid black; -} -td.displaylines { - text-align: center; - white-space: nowrap; -} -.centerline { - text-align: center; -} -.rightline { - text-align: right; -} -div.verbatim { - font-family: monospace; - white-space: nowrap; -} -table.verbatim { - width: 100%; -} -.fbox { - background: url(note.png) no-repeat #cec; - padding-left: 65px; - padding-top: 1em; - padding-bottom: 1em; - padding-right: 1em; - text-indent: 0pt; - border: dotted black 1px; -} -div.center div.fbox { - text-align: center; - clear: both; - padding-left: 3.0pt; - padding-right: 3.0pt; - text-indent: 0pt; - border: solid black 0.4pt; -} -table.minipage { - width: 100%; -} -div.center, div.center div.center { - text-align: center; - margin-left: 1em; - margin-right: 1em; -} -div.center div { - text-align: left; -} -div.flushright, div.flushright div.flushright { - text-align: right; -} -div.flushright div { - text-align: left; -} -div.flushleft { - text-align: left; -} -.underline { - text-decoration: underline; -} -.underline img { - border-bottom: 1px solid black; - margin-bottom: 1pt; -} -.framebox-c, .framebox-l, .framebox-r { - padding-left: 3.0pt; - padding-right: 3.0pt; - text-indent: 0pt; - border: solid black 0.4pt; -} -.framebox-c { - text-align: center; -} -.framebox-l { - text-align: left; -} -.framebox-r { - text-align: right; -} -span.thank-mark { - vertical-align: super -} -span.footnote-mark sup.textsuperscript, span.footnote-mark a sup.textsuperscript { - font-size: 80%; -} -div.tabular, div.center div.tabular { - text-align: center; - margin-top: 0.5em; - margin-bottom: 0.5em; -} -table.tabular td p { - margin-top: 0em; -} -table.tabular { - margin-left: auto; - margin-right: auto; -} -div.td00 { - margin-left: 0pt; - margin-right: 0pt; -} -div.td01 { - margin-left: 0pt; - margin-right: 5pt; -} -div.td10 { - margin-left: 5pt; - margin-right: 0pt; -} -div.td11 { - margin-left: 5pt; - margin-right: 5pt; -} -table[rules] { - border-left: solid black 0.4pt; - border-right: solid black 0.4pt; -} -td.td00 { - padding-left: 0pt; - padding-right: 0pt; -} -td.td01 { - padding-left: 0pt; - padding-right: 5pt; -} -td.td10 { - padding-left: 5pt; - padding-right: 0pt; -} -td.td11 { - padding-left: 5pt; - padding-right: 5pt; -} -table[rules] { - border-left: solid black 0.4pt; - border-right: solid black 0.4pt; -} -.hline hr, .cline hr { - height : 1px; - margin: 0px; -} -.tabbing-right { - text-align: right; -} -span.TEX { - letter-spacing: -0.125em; -} -span.TEX span.E { - position: relative;top: 0.5ex;left: -0.0417em; -} -a span.TEX span.E { - text-decoration: none; -} -span.LATEX span.A { - position: relative; - top: -0.5ex; - left: -0.4em; - font-size: 85%; -} -span.LATEX span.TEX { - position: relative; - left: -0.4em; -} -div.float img, div.float .caption { - text-align: center; -} -div.figure img, div.figure .caption { - text-align: center; -} -.marginpar { - width: 20%; - float: right; - text-align: left; - margin-left: auto; - margin-top: 0.5em; - font-size: 85%; - text-decoration: underline; -} -.marginpar p { - margin-top: 0.4em; - margin-bottom: 0.4em; -} -table.equation { - width: 100%; -} -.equation td { - text-align: center; -} -td.equation { - margin-top: 1em; - margin-bottom: 1em; -} -td.equation-label { - width: 5%; - text-align: center; -} -td.eqnarray4 { - width: 5%; - white-space: normal; -} -td.eqnarray2 { - width: 5%; -} -table.eqnarray-star, table.eqnarray { - width: 100%; -} -div.eqnarray { - text-align: center; -} -div.array { - text-align: center; -} -div.pmatrix { - text-align: center; -} -table.pmatrix { - width: 100%; -} -span.pmatrix img { - vertical-align: middle; -} -div.pmatrix { - text-align: center; -} -table.pmatrix { - width: 100%; -} -img.cdots { - vertical-align: middle; -} -.partToc a, .partToc, .likepartToc a, .likepartToc { - line-height: 200%; - font-weight: bold; - font-size: 110%; -} -.chapterToc a, .chapterToc, .likechapterToc a, .likechapterToc, .appendixToc a, .appendixToc { - line-height: 200%; - font-weight: bold; -} -.caption td.id { - font-weight: bold; - white-space: nowrap; -} -table.caption { - text-align: center; -} -h1.partHead { - text-align: center; -} -p.bibitem { - text-indent: -2em; - margin-left: 2em; - margin-top: 0.6em; - margin-bottom: 0.6em; -} -p.bibitem-p { - text-indent: 0em; - margin-left: 2em; - margin-top: 0.6em; - margin-bottom: 0.6em; -} -.paragraphHead, .likeparagraphHead { - margin-top: 2em; - font-weight: bold; -} -.subparagraphHead, .likesubparagraphHead { - font-weight: bold; -} -.quote { - margin-bottom: 0.25em; - margin-top: 0.25em; - margin-left: 1em; - margin-right: 1em; - text-align: justify; -} -.verse { - white-space: nowrap; - margin-left: 2em} -div.maketitle { - text-align: center; -} -h2.titleHead { - text-align: center; -} -div.maketitle { - margin-bottom: 2em; -} -div.author, div.date { - text-align: center; -} -div.thanks { - text-align: left; - margin-left: 10%; - font-size: 85%; - font-style: italic; -} -div.author { - white-space: nowrap; -} -.quotation { - margin-bottom: 0.25em; - margin-top: 0.25em; - margin-left: 1em; -} -h1.partHead { - text-align: center; -} -img.graphics { - margin-left: 10%; -} -.figure { - width: 100%; -} -P.fancyvrb { - white-space: nowrap; -} -hr { - border: 0; - height: 1px; -} -div#fancyvrb { - white-space: nowrap; - background: #eee; - padding: 1em; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/hgext.tex --- a/en/hgext.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ -\chapter{Adding functionality with extensions} -\label{chap:hgext} - -While the core of Mercurial is quite complete from a functionality -standpoint, it's deliberately shorn of fancy features. This approach -of preserving simplicity keeps the software easy to deal with for both -maintainers and users. - -However, Mercurial doesn't box you in with an inflexible command set: -you can add features to it as \emph{extensions} (sometimes known as -\emph{plugins}). We've already discussed a few of these extensions in -earlier chapters. -\begin{itemize} -\item Section~\ref{sec:tour-merge:fetch} covers the \hgext{fetch} - extension; this combines pulling new changes and merging them with - local changes into a single command, \hgxcmd{fetch}{fetch}. -\item In chapter~\ref{chap:hook}, we covered several extensions that - are useful for hook-related functionality: \hgext{acl} adds access - control lists; \hgext{bugzilla} adds integration with the Bugzilla - bug tracking system; and \hgext{notify} sends notification emails on - new changes. -\item The Mercurial Queues patch management extension is so invaluable - that it merits two chapters and an appendix all to itself. - Chapter~\ref{chap:mq} covers the basics; - chapter~\ref{chap:mq-collab} discusses advanced topics; and - appendix~\ref{chap:mqref} goes into detail on each command. -\end{itemize} - -In this chapter, we'll cover some of the other extensions that are -available for Mercurial, and briefly touch on some of the machinery -you'll need to know about if you want to write an extension of your -own. -\begin{itemize} -\item In section~\ref{sec:hgext:inotify}, we'll discuss the - possibility of \emph{huge} performance improvements using the - \hgext{inotify} extension. -\end{itemize} - -\section{Improve performance with the \hgext{inotify} extension} -\label{sec:hgext:inotify} - -Are you interested in having some of the most common Mercurial -operations run as much as a hundred times faster? Read on! - -Mercurial has great performance under normal circumstances. For -example, when you run the \hgcmd{status} command, Mercurial has to -scan almost every directory and file in your repository so that it can -display file status. Many other Mercurial commands need to do the -same work behind the scenes; for example, the \hgcmd{diff} command -uses the status machinery to avoid doing an expensive comparison -operation on files that obviously haven't changed. - -Because obtaining file status is crucial to good performance, the -authors of Mercurial have optimised this code to within an inch of its -life. However, there's no avoiding the fact that when you run -\hgcmd{status}, Mercurial is going to have to perform at least one -expensive system call for each managed file to determine whether it's -changed since the last time Mercurial checked. For a sufficiently -large repository, this can take a long time. - -To put a number on the magnitude of this effect, I created a -repository containing 150,000 managed files. I timed \hgcmd{status} -as taking ten seconds to run, even when \emph{none} of those files had -been modified. - -Many modern operating systems contain a file notification facility. -If a program signs up to an appropriate service, the operating system -will notify it every time a file of interest is created, modified, or -deleted. On Linux systems, the kernel component that does this is -called \texttt{inotify}. - -Mercurial's \hgext{inotify} extension talks to the kernel's -\texttt{inotify} component to optimise \hgcmd{status} commands. The -extension has two components. A daemon sits in the background and -receives notifications from the \texttt{inotify} subsystem. It also -listens for connections from a regular Mercurial command. The -extension modifies Mercurial's behaviour so that instead of scanning -the filesystem, it queries the daemon. Since the daemon has perfect -information about the state of the repository, it can respond with a -result instantaneously, avoiding the need to scan every directory and -file in the repository. - -Recall the ten seconds that I measured plain Mercurial as taking to -run \hgcmd{status} on a 150,000 file repository. With the -\hgext{inotify} extension enabled, the time dropped to 0.1~seconds, a -factor of \emph{one hundred} faster. - -Before we continue, please pay attention to some caveats. -\begin{itemize} -\item The \hgext{inotify} extension is Linux-specific. Because it - interfaces directly to the Linux kernel's \texttt{inotify} - subsystem, it does not work on other operating systems. -\item It should work on any Linux distribution that was released after - early~2005. Older distributions are likely to have a kernel that - lacks \texttt{inotify}, or a version of \texttt{glibc} that does not - have the necessary interfacing support. -\item Not all filesystems are suitable for use with the - \hgext{inotify} extension. Network filesystems such as NFS are a - non-starter, for example, particularly if you're running Mercurial - on several systems, all mounting the same network filesystem. The - kernel's \texttt{inotify} system has no way of knowing about changes - made on another system. Most local filesystems (e.g.~ext3, XFS, - ReiserFS) should work fine. -\end{itemize} - -The \hgext{inotify} extension is not yet shipped with Mercurial as of -May~2007, so it's a little more involved to set up than other -extensions. But the performance improvement is worth it! - -The extension currently comes in two parts: a set of patches to the -Mercurial source code, and a library of Python bindings to the -\texttt{inotify} subsystem. -\begin{note} - There are \emph{two} Python \texttt{inotify} binding libraries. One - of them is called \texttt{pyinotify}, and is packaged by some Linux - distributions as \texttt{python-inotify}. This is \emph{not} the - one you'll need, as it is too buggy and inefficient to be practical. -\end{note} -To get going, it's best to already have a functioning copy of -Mercurial installed. -\begin{note} - If you follow the instructions below, you'll be \emph{replacing} and - overwriting any existing installation of Mercurial that you might - already have, using the latest ``bleeding edge'' Mercurial code. - Don't say you weren't warned! -\end{note} -\begin{enumerate} -\item Clone the Python \texttt{inotify} binding repository. Build and - install it. - \begin{codesample4} - hg clone http://hg.kublai.com/python/inotify - cd inotify - python setup.py build --force - sudo python setup.py install --skip-build - \end{codesample4} -\item Clone the \dirname{crew} Mercurial repository. Clone the - \hgext{inotify} patch repository so that Mercurial Queues will be - able to apply patches to your cope of the \dirname{crew} repository. - \begin{codesample4} - hg clone http://hg.intevation.org/mercurial/crew - hg clone crew inotify - hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches - \end{codesample4} -\item Make sure that you have the Mercurial Queues extension, - \hgext{mq}, enabled. If you've never used MQ, read - section~\ref{sec:mq:start} to get started quickly. -\item Go into the \dirname{inotify} repo, and apply all of the - \hgext{inotify} patches using the \hgxopt{mq}{qpush}{-a} option to - the \hgxcmd{mq}{qpush} command. - \begin{codesample4} - cd inotify - hg qpush -a - \end{codesample4} - If you get an error message from \hgxcmd{mq}{qpush}, you should not - continue. Instead, ask for help. -\item Build and install the patched version of Mercurial. - \begin{codesample4} - python setup.py build --force - sudo python setup.py install --skip-build - \end{codesample4} -\end{enumerate} -Once you've build a suitably patched version of Mercurial, all you -need to do to enable the \hgext{inotify} extension is add an entry to -your \hgrc. -\begin{codesample2} - [extensions] - inotify = -\end{codesample2} -When the \hgext{inotify} extension is enabled, Mercurial will -automatically and transparently start the status daemon the first time -you run a command that needs status in a repository. It runs one -status daemon per repository. - -The status daemon is started silently, and runs in the background. If -you look at a list of running processes after you've enabled the -\hgext{inotify} extension and run a few commands in different -repositories, you'll thus see a few \texttt{hg} processes sitting -around, waiting for updates from the kernel and queries from -Mercurial. - -The first time you run a Mercurial command in a repository when you -have the \hgext{inotify} extension enabled, it will run with about the -same performance as a normal Mercurial command. This is because the -status daemon needs to perform a normal status scan so that it has a -baseline against which to apply later updates from the kernel. -However, \emph{every} subsequent command that does any kind of status -check should be noticeably faster on repositories of even fairly -modest size. Better yet, the bigger your repository is, the greater a -performance advantage you'll see. The \hgext{inotify} daemon makes -status operations almost instantaneous on repositories of all sizes! - -If you like, you can manually start a status daemon using the -\hgxcmd{inotify}{inserve} command. This gives you slightly finer -control over how the daemon ought to run. This command will of course -only be available when the \hgext{inotify} extension is enabled. - -When you're using the \hgext{inotify} extension, you should notice -\emph{no difference at all} in Mercurial's behaviour, with the sole -exception of status-related commands running a whole lot faster than -they used to. You should specifically expect that commands will not -print different output; neither should they give different results. -If either of these situations occurs, please report a bug. - -\section{Flexible diff support with the \hgext{extdiff} extension} -\label{sec:hgext:extdiff} - -Mercurial's built-in \hgcmd{diff} command outputs plaintext unified -diffs. -\interaction{extdiff.diff} -If you would like to use an external tool to display modifications, -you'll want to use the \hgext{extdiff} extension. This will let you -use, for example, a graphical diff tool. - -The \hgext{extdiff} extension is bundled with Mercurial, so it's easy -to set up. In the \rcsection{extensions} section of your \hgrc, -simply add a one-line entry to enable the extension. -\begin{codesample2} - [extensions] - extdiff = -\end{codesample2} -This introduces a command named \hgxcmd{extdiff}{extdiff}, which by -default uses your system's \command{diff} command to generate a -unified diff in the same form as the built-in \hgcmd{diff} command. -\interaction{extdiff.extdiff} -The result won't be exactly the same as with the built-in \hgcmd{diff} -variations, because the output of \command{diff} varies from one -system to another, even when passed the same options. - -As the ``\texttt{making snapshot}'' lines of output above imply, the -\hgxcmd{extdiff}{extdiff} command works by creating two snapshots of -your source tree. The first snapshot is of the source revision; the -second, of the target revision or working directory. The -\hgxcmd{extdiff}{extdiff} command generates these snapshots in a -temporary directory, passes the name of each directory to an external -diff viewer, then deletes the temporary directory. For efficiency, it -only snapshots the directories and files that have changed between the -two revisions. - -Snapshot directory names have the same base name as your repository. -If your repository path is \dirname{/quux/bar/foo}, then \dirname{foo} -will be the name of each snapshot directory. Each snapshot directory -name has its changeset ID appended, if appropriate. If a snapshot is -of revision \texttt{a631aca1083f}, the directory will be named -\dirname{foo.a631aca1083f}. A snapshot of the working directory won't -have a changeset ID appended, so it would just be \dirname{foo} in -this example. To see what this looks like in practice, look again at -the \hgxcmd{extdiff}{extdiff} example above. Notice that the diff has -the snapshot directory names embedded in its header. - -The \hgxcmd{extdiff}{extdiff} command accepts two important options. -The \hgxopt{extdiff}{extdiff}{-p} option lets you choose a program to -view differences with, instead of \command{diff}. With the -\hgxopt{extdiff}{extdiff}{-o} option, you can change the options that -\hgxcmd{extdiff}{extdiff} passes to the program (by default, these -options are ``\texttt{-Npru}'', which only make sense if you're -running \command{diff}). In other respects, the -\hgxcmd{extdiff}{extdiff} command acts similarly to the built-in -\hgcmd{diff} command: you use the same option names, syntax, and -arguments to specify the revisions you want, the files you want, and -so on. - -As an example, here's how to run the normal system \command{diff} -command, getting it to generate context diffs (using the -\cmdopt{diff}{-c} option) instead of unified diffs, and five lines of -context instead of the default three (passing \texttt{5} as the -argument to the \cmdopt{diff}{-C} option). -\interaction{extdiff.extdiff-ctx} - -Launching a visual diff tool is just as easy. Here's how to launch -the \command{kdiff3} viewer. -\begin{codesample2} - hg extdiff -p kdiff3 -o '' -\end{codesample2} - -If your diff viewing command can't deal with directories, you can -easily work around this with a little scripting. For an example of -such scripting in action with the \hgext{mq} extension and the -\command{interdiff} command, see -section~\ref{mq-collab:tips:interdiff}. - -\subsection{Defining command aliases} - -It can be cumbersome to remember the options to both the -\hgxcmd{extdiff}{extdiff} command and the diff viewer you want to use, -so the \hgext{extdiff} extension lets you define \emph{new} commands -that will invoke your diff viewer with exactly the right options. - -All you need to do is edit your \hgrc, and add a section named -\rcsection{extdiff}. Inside this section, you can define multiple -commands. Here's how to add a \texttt{kdiff3} command. Once you've -defined this, you can type ``\texttt{hg kdiff3}'' and the -\hgext{extdiff} extension will run \command{kdiff3} for you. -\begin{codesample2} - [extdiff] - cmd.kdiff3 = -\end{codesample2} -If you leave the right hand side of the definition empty, as above, -the \hgext{extdiff} extension uses the name of the command you defined -as the name of the external program to run. But these names don't -have to be the same. Here, we define a command named ``\texttt{hg - wibble}'', which runs \command{kdiff3}. -\begin{codesample2} - [extdiff] - cmd.wibble = kdiff3 -\end{codesample2} - -You can also specify the default options that you want to invoke your -diff viewing program with. The prefix to use is ``\texttt{opts.}'', -followed by the name of the command to which the options apply. This -example defines a ``\texttt{hg vimdiff}'' command that runs the -\command{vim} editor's \texttt{DirDiff} extension. -\begin{codesample2} - [extdiff] - cmd.vimdiff = vim - opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' -\end{codesample2} - -\section{Cherrypicking changes with the \hgext{transplant} extension} -\label{sec:hgext:transplant} - -Need to have a long chat with Brendan about this. - -\section{Send changes via email with the \hgext{patchbomb} extension} -\label{sec:hgext:patchbomb} - -Many projects have a culture of ``change review'', in which people -send their modifications to a mailing list for others to read and -comment on before they commit the final version to a shared -repository. Some projects have people who act as gatekeepers; they -apply changes from other people to a repository to which those others -don't have access. - -Mercurial makes it easy to send changes over email for review or -application, via its \hgext{patchbomb} extension. The extension is so -namd because changes are formatted as patches, and it's usual to send -one changeset per email message. Sending a long series of changes by -email is thus much like ``bombing'' the recipient's inbox, hence -``patchbomb''. - -As usual, the basic configuration of the \hgext{patchbomb} extension -takes just one or two lines in your \hgrc. -\begin{codesample2} - [extensions] - patchbomb = -\end{codesample2} -Once you've enabled the extension, you will have a new command -available, named \hgxcmd{patchbomb}{email}. - -The safest and best way to invoke the \hgxcmd{patchbomb}{email} -command is to \emph{always} run it first with the -\hgxopt{patchbomb}{email}{-n} option. This will show you what the -command \emph{would} send, without actually sending anything. Once -you've had a quick glance over the changes and verified that you are -sending the right ones, you can rerun the same command, with the -\hgxopt{patchbomb}{email}{-n} option removed. - -The \hgxcmd{patchbomb}{email} command accepts the same kind of -revision syntax as every other Mercurial command. For example, this -command will send every revision between 7 and \texttt{tip}, -inclusive. -\begin{codesample2} - hg email -n 7:tip -\end{codesample2} -You can also specify a \emph{repository} to compare with. If you -provide a repository but no revisions, the \hgxcmd{patchbomb}{email} -command will send all revisions in the local repository that are not -present in the remote repository. If you additionally specify -revisions or a branch name (the latter using the -\hgxopt{patchbomb}{email}{-b} option), this will constrain the -revisions sent. - -It's perfectly safe to run the \hgxcmd{patchbomb}{email} command -without the names of the people you want to send to: if you do this, -it will just prompt you for those values interactively. (If you're -using a Linux or Unix-like system, you should have enhanced -\texttt{readline}-style editing capabilities when entering those -headers, too, which is useful.) - -When you are sending just one revision, the \hgxcmd{patchbomb}{email} -command will by default use the first line of the changeset -description as the subject of the single email message it sends. - -If you send multiple revisions, the \hgxcmd{patchbomb}{email} command -will usually send one message per changeset. It will preface the -series with an introductory message, in which you should describe the -purpose of the series of changes you're sending. - -\subsection{Changing the behaviour of patchbombs} - -Not every project has exactly the same conventions for sending changes -in email; the \hgext{patchbomb} extension tries to accommodate a -number of variations through command line options. -\begin{itemize} -\item You can write a subject for the introductory message on the - command line using the \hgxopt{patchbomb}{email}{-s} option. This - takes one argument, the text of the subject to use. -\item To change the email address from which the messages originate, - use the \hgxopt{patchbomb}{email}{-f} option. This takes one - argument, the email address to use. -\item The default behaviour is to send unified diffs (see - section~\ref{sec:mq:patch} for a description of the format), one per - message. You can send a binary bundle instead with the - \hgxopt{patchbomb}{email}{-b} option. -\item Unified diffs are normally prefaced with a metadata header. You - can omit this, and send unadorned diffs, with the - \hgxopt{patchbomb}{email}{--plain} option. -\item Diffs are normally sent ``inline'', in the same body part as the - description of a patch. This makes it easiest for the largest - number of readers to quote and respond to parts of a diff, as some - mail clients will only quote the first MIME body part in a message. - If you'd prefer to send the description and the diff in separate - body parts, use the \hgxopt{patchbomb}{email}{-a} option. -\item Instead of sending mail messages, you can write them to an - \texttt{mbox}-format mail folder using the - \hgxopt{patchbomb}{email}{-m} option. That option takes one - argument, the name of the file to write to. -\item If you would like to add a \command{diffstat}-format summary to - each patch, and one to the introductory message, use the - \hgxopt{patchbomb}{email}{-d} option. The \command{diffstat} - command displays a table containing the name of each file patched, - the number of lines affected, and a histogram showing how much each - file is modified. This gives readers a qualitative glance at how - complex a patch is. -\end{itemize} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/hook.tex --- a/en/hook.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1413 +0,0 @@ -\chapter{Handling repository events with hooks} -\label{chap:hook} - -Mercurial offers a powerful mechanism to let you perform automated -actions in response to events that occur in a repository. In some -cases, you can even control Mercurial's response to those events. - -The name Mercurial uses for one of these actions is a \emph{hook}. -Hooks are called ``triggers'' in some revision control systems, but -the two names refer to the same idea. - -\section{An overview of hooks in Mercurial} - -Here is a brief list of the hooks that Mercurial supports. We will -revisit each of these hooks in more detail later, in -section~\ref{sec:hook:ref}. - -\begin{itemize} -\item[\small\hook{changegroup}] This is run after a group of - changesets has been brought into the repository from elsewhere. -\item[\small\hook{commit}] This is run after a new changeset has been - created in the local repository. -\item[\small\hook{incoming}] This is run once for each new changeset - that is brought into the repository from elsewhere. Notice the - difference from \hook{changegroup}, which is run once per - \emph{group} of changesets brought in. -\item[\small\hook{outgoing}] This is run after a group of changesets - has been transmitted from this repository. -\item[\small\hook{prechangegroup}] This is run before starting to - bring a group of changesets into the repository. -\item[\small\hook{precommit}] Controlling. This is run before starting - a commit. -\item[\small\hook{preoutgoing}] Controlling. This is run before - starting to transmit a group of changesets from this repository. -\item[\small\hook{pretag}] Controlling. This is run before creating a tag. -\item[\small\hook{pretxnchangegroup}] Controlling. This is run after a - group of changesets has been brought into the local repository from - another, but before the transaction completes that will make the - changes permanent in the repository. -\item[\small\hook{pretxncommit}] Controlling. This is run after a new - changeset has been created in the local repository, but before the - transaction completes that will make it permanent. -\item[\small\hook{preupdate}] Controlling. This is run before starting - an update or merge of the working directory. -\item[\small\hook{tag}] This is run after a tag is created. -\item[\small\hook{update}] This is run after an update or merge of the - working directory has finished. -\end{itemize} -Each of the hooks whose description begins with the word -``Controlling'' has the ability to determine whether an activity can -proceed. If the hook succeeds, the activity may proceed; if it fails, -the activity is either not permitted or undone, depending on the hook. - -\section{Hooks and security} - -\subsection{Hooks are run with your privileges} - -When you run a Mercurial command in a repository, and the command -causes a hook to run, that hook runs on \emph{your} system, under -\emph{your} user account, with \emph{your} privilege level. Since -hooks are arbitrary pieces of executable code, you should treat them -with an appropriate level of suspicion. Do not install a hook unless -you are confident that you know who created it and what it does. - -In some cases, you may be exposed to hooks that you did not install -yourself. If you work with Mercurial on an unfamiliar system, -Mercurial will run hooks defined in that system's global \hgrc\ file. - -If you are working with a repository owned by another user, Mercurial -can run hooks defined in that user's repository, but it will still run -them as ``you''. For example, if you \hgcmd{pull} from that -repository, and its \sfilename{.hg/hgrc} defines a local -\hook{outgoing} hook, that hook will run under your user account, even -though you don't own that repository. - -\begin{note} - This only applies if you are pulling from a repository on a local or - network filesystem. If you're pulling over http or ssh, any - \hook{outgoing} hook will run under whatever account is executing - the server process, on the server. -\end{note} - -XXX To see what hooks are defined in a repository, use the -\hgcmdargs{config}{hooks} command. If you are working in one -repository, but talking to another that you do not own (e.g.~using -\hgcmd{pull} or \hgcmd{incoming}), remember that it is the other -repository's hooks you should be checking, not your own. - -\subsection{Hooks do not propagate} - -In Mercurial, hooks are not revision controlled, and do not propagate -when you clone, or pull from, a repository. The reason for this is -simple: a hook is a completely arbitrary piece of executable code. It -runs under your user identity, with your privilege level, on your -machine. - -It would be extremely reckless for any distributed revision control -system to implement revision-controlled hooks, as this would offer an -easily exploitable way to subvert the accounts of users of the -revision control system. - -Since Mercurial does not propagate hooks, if you are collaborating -with other people on a common project, you should not assume that they -are using the same Mercurial hooks as you are, or that theirs are -correctly configured. You should document the hooks you expect people -to use. - -In a corporate intranet, this is somewhat easier to control, as you -can for example provide a ``standard'' installation of Mercurial on an -NFS filesystem, and use a site-wide \hgrc\ file to define hooks that -all users will see. However, this too has its limits; see below. - -\subsection{Hooks can be overridden} - -Mercurial allows you to override a hook definition by redefining the -hook. You can disable it by setting its value to the empty string, or -change its behaviour as you wish. - -If you deploy a system-~or site-wide \hgrc\ file that defines some -hooks, you should thus understand that your users can disable or -override those hooks. - -\subsection{Ensuring that critical hooks are run} - -Sometimes you may want to enforce a policy that you do not want others -to be able to work around. For example, you may have a requirement -that every changeset must pass a rigorous set of tests. Defining this -requirement via a hook in a site-wide \hgrc\ won't work for remote -users on laptops, and of course local users can subvert it at will by -overriding the hook. - -Instead, you can set up your policies for use of Mercurial so that -people are expected to propagate changes through a well-known -``canonical'' server that you have locked down and configured -appropriately. - -One way to do this is via a combination of social engineering and -technology. Set up a restricted-access account; users can push -changes over the network to repositories managed by this account, but -they cannot log into the account and run normal shell commands. In -this scenario, a user can commit a changeset that contains any old -garbage they want. - -When someone pushes a changeset to the server that everyone pulls -from, the server will test the changeset before it accepts it as -permanent, and reject it if it fails to pass the test suite. If -people only pull changes from this filtering server, it will serve to -ensure that all changes that people pull have been automatically -vetted. - -\section{Care with \texttt{pretxn} hooks in a shared-access repository} - -If you want to use hooks to do some automated work in a repository -that a number of people have shared access to, you need to be careful -in how you do this. - -Mercurial only locks a repository when it is writing to the -repository, and only the parts of Mercurial that write to the -repository pay attention to locks. Write locks are necessary to -prevent multiple simultaneous writers from scribbling on each other's -work, corrupting the repository. - -Because Mercurial is careful with the order in which it reads and -writes data, it does not need to acquire a lock when it wants to read -data from the repository. The parts of Mercurial that read from the -repository never pay attention to locks. This lockless reading scheme -greatly increases performance and concurrency. - -With great performance comes a trade-off, though, one which has the -potential to cause you trouble unless you're aware of it. To describe -this requires a little detail about how Mercurial adds changesets to a -repository and reads those changes. - -When Mercurial \emph{writes} metadata, it writes it straight into the -destination file. It writes file data first, then manifest data -(which contains pointers to the new file data), then changelog data -(which contains pointers to the new manifest data). Before the first -write to each file, it stores a record of where the end of the file -was in its transaction log. If the transaction must be rolled back, -Mercurial simply truncates each file back to the size it was before the -transaction began. - -When Mercurial \emph{reads} metadata, it reads the changelog first, -then everything else. Since a reader will only access parts of the -manifest or file metadata that it can see in the changelog, it can -never see partially written data. - -Some controlling hooks (\hook{pretxncommit} and -\hook{pretxnchangegroup}) run when a transaction is almost complete. -All of the metadata has been written, but Mercurial can still roll the -transaction back and cause the newly-written data to disappear. - -If one of these hooks runs for long, it opens a window of time during -which a reader can see the metadata for changesets that are not yet -permanent, and should not be thought of as ``really there''. The -longer the hook runs, the longer that window is open. - -\subsection{The problem illustrated} - -In principle, a good use for the \hook{pretxnchangegroup} hook would -be to automatically build and test incoming changes before they are -accepted into a central repository. This could let you guarantee that -nobody can push changes to this repository that ``break the build''. -But if a client can pull changes while they're being tested, the -usefulness of the test is zero; an unsuspecting someone can pull -untested changes, potentially breaking their build. - -The safest technological answer to this challenge is to set up such a -``gatekeeper'' repository as \emph{unidirectional}. Let it take -changes pushed in from the outside, but do not allow anyone to pull -changes from it (use the \hook{preoutgoing} hook to lock it down). -Configure a \hook{changegroup} hook so that if a build or test -succeeds, the hook will push the new changes out to another repository -that people \emph{can} pull from. - -In practice, putting a centralised bottleneck like this in place is -not often a good idea, and transaction visibility has nothing to do -with the problem. As the size of a project---and the time it takes to -build and test---grows, you rapidly run into a wall with this ``try -before you buy'' approach, where you have more changesets to test than -time in which to deal with them. The inevitable result is frustration -on the part of all involved. - -An approach that scales better is to get people to build and test -before they push, then run automated builds and tests centrally -\emph{after} a push, to be sure all is well. The advantage of this -approach is that it does not impose a limit on the rate at which the -repository can accept changes. - -\section{A short tutorial on using hooks} -\label{sec:hook:simple} - -It is easy to write a Mercurial hook. Let's start with a hook that -runs when you finish a \hgcmd{commit}, and simply prints the hash of -the changeset you just created. The hook is called \hook{commit}. - -\begin{figure}[ht] - \interaction{hook.simple.init} - \caption{A simple hook that runs when a changeset is committed} - \label{ex:hook:init} -\end{figure} - -All hooks follow the pattern in example~\ref{ex:hook:init}. You add -an entry to the \rcsection{hooks} section of your \hgrc\. On the left -is the name of the event to trigger on; on the right is the action to -take. As you can see, you can run an arbitrary shell command in a -hook. Mercurial passes extra information to the hook using -environment variables (look for \envar{HG\_NODE} in the example). - -\subsection{Performing multiple actions per event} - -Quite often, you will want to define more than one hook for a -particular kind of event, as shown in example~\ref{ex:hook:ext}. -Mercurial lets you do this by adding an \emph{extension} to the end of -a hook's name. You extend a hook's name by giving the name of the -hook, followed by a full stop (the ``\texttt{.}'' character), followed -by some more text of your choosing. For example, Mercurial will run -both \texttt{commit.foo} and \texttt{commit.bar} when the -\texttt{commit} event occurs. - -\begin{figure}[ht] - \interaction{hook.simple.ext} - \caption{Defining a second \hook{commit} hook} - \label{ex:hook:ext} -\end{figure} - -To give a well-defined order of execution when there are multiple -hooks defined for an event, Mercurial sorts hooks by extension, and -executes the hook commands in this sorted order. In the above -example, it will execute \texttt{commit.bar} before -\texttt{commit.foo}, and \texttt{commit} before both. - -It is a good idea to use a somewhat descriptive extension when you -define a new hook. This will help you to remember what the hook was -for. If the hook fails, you'll get an error message that contains the -hook name and extension, so using a descriptive extension could give -you an immediate hint as to why the hook failed (see -section~\ref{sec:hook:perm} for an example). - -\subsection{Controlling whether an activity can proceed} -\label{sec:hook:perm} - -In our earlier examples, we used the \hook{commit} hook, which is -run after a commit has completed. This is one of several Mercurial -hooks that run after an activity finishes. Such hooks have no way of -influencing the activity itself. - -Mercurial defines a number of events that occur before an activity -starts; or after it starts, but before it finishes. Hooks that -trigger on these events have the added ability to choose whether the -activity can continue, or will abort. - -The \hook{pretxncommit} hook runs after a commit has all but -completed. In other words, the metadata representing the changeset -has been written out to disk, but the transaction has not yet been -allowed to complete. The \hook{pretxncommit} hook has the ability to -decide whether the transaction can complete, or must be rolled back. - -If the \hook{pretxncommit} hook exits with a status code of zero, the -transaction is allowed to complete; the commit finishes; and the -\hook{commit} hook is run. If the \hook{pretxncommit} hook exits with -a non-zero status code, the transaction is rolled back; the metadata -representing the changeset is erased; and the \hook{commit} hook is -not run. - -\begin{figure}[ht] - \interaction{hook.simple.pretxncommit} - \caption{Using the \hook{pretxncommit} hook to control commits} - \label{ex:hook:pretxncommit} -\end{figure} - -The hook in example~\ref{ex:hook:pretxncommit} checks that a commit -comment contains a bug ID. If it does, the commit can complete. If -not, the commit is rolled back. - -\section{Writing your own hooks} - -When you are writing a hook, you might find it useful to run Mercurial -either with the \hggopt{-v} option, or the \rcitem{ui}{verbose} config -item set to ``true''. When you do so, Mercurial will print a message -before it calls each hook. - -\subsection{Choosing how your hook should run} -\label{sec:hook:lang} - -You can write a hook either as a normal program---typically a shell -script---or as a Python function that is executed within the Mercurial -process. - -Writing a hook as an external program has the advantage that it -requires no knowledge of Mercurial's internals. You can call normal -Mercurial commands to get any added information you need. The -trade-off is that external hooks are slower than in-process hooks. - -An in-process Python hook has complete access to the Mercurial API, -and does not ``shell out'' to another process, so it is inherently -faster than an external hook. It is also easier to obtain much of the -information that a hook requires by using the Mercurial API than by -running Mercurial commands. - -If you are comfortable with Python, or require high performance, -writing your hooks in Python may be a good choice. However, when you -have a straightforward hook to write and you don't need to care about -performance (probably the majority of hooks), a shell script is -perfectly fine. - -\subsection{Hook parameters} -\label{sec:hook:param} - -Mercurial calls each hook with a set of well-defined parameters. In -Python, a parameter is passed as a keyword argument to your hook -function. For an external program, a parameter is passed as an -environment variable. - -Whether your hook is written in Python or as a shell script, the -hook-specific parameter names and values will be the same. A boolean -parameter will be represented as a boolean value in Python, but as the -number 1 (for ``true'') or 0 (for ``false'') as an environment -variable for an external hook. If a hook parameter is named -\texttt{foo}, the keyword argument for a Python hook will also be -named \texttt{foo}, while the environment variable for an external -hook will be named \texttt{HG\_FOO}. - -\subsection{Hook return values and activity control} - -A hook that executes successfully must exit with a status of zero if -external, or return boolean ``false'' if in-process. Failure is -indicated with a non-zero exit status from an external hook, or an -in-process hook returning boolean ``true''. If an in-process hook -raises an exception, the hook is considered to have failed. - -For a hook that controls whether an activity can proceed, zero/false -means ``allow'', while non-zero/true/exception means ``deny''. - -\subsection{Writing an external hook} - -When you define an external hook in your \hgrc\ and the hook is run, -its value is passed to your shell, which interprets it. This means -that you can use normal shell constructs in the body of the hook. - -An executable hook is always run with its current directory set to a -repository's root directory. - -Each hook parameter is passed in as an environment variable; the name -is upper-cased, and prefixed with the string ``\texttt{HG\_}''. - -With the exception of hook parameters, Mercurial does not set or -modify any environment variables when running a hook. This is useful -to remember if you are writing a site-wide hook that may be run by a -number of different users with differing environment variables set. -In multi-user situations, you should not rely on environment variables -being set to the values you have in your environment when testing the -hook. - -\subsection{Telling Mercurial to use an in-process hook} - -The \hgrc\ syntax for defining an in-process hook is slightly -different than for an executable hook. The value of the hook must -start with the text ``\texttt{python:}'', and continue with the -fully-qualified name of a callable object to use as the hook's value. - -The module in which a hook lives is automatically imported when a hook -is run. So long as you have the module name and \envar{PYTHONPATH} -right, it should ``just work''. - -The following \hgrc\ example snippet illustrates the syntax and -meaning of the notions we just described. -\begin{codesample2} - [hooks] - commit.example = python:mymodule.submodule.myhook -\end{codesample2} -When Mercurial runs the \texttt{commit.example} hook, it imports -\texttt{mymodule.submodule}, looks for the callable object named -\texttt{myhook}, and calls it. - -\subsection{Writing an in-process hook} - -The simplest in-process hook does nothing, but illustrates the basic -shape of the hook API: -\begin{codesample2} - def myhook(ui, repo, **kwargs): - pass -\end{codesample2} -The first argument to a Python hook is always a -\pymodclass{mercurial.ui}{ui} object. The second is a repository object; -at the moment, it is always an instance of -\pymodclass{mercurial.localrepo}{localrepository}. Following these two -arguments are other keyword arguments. Which ones are passed in -depends on the hook being called, but a hook can ignore arguments it -doesn't care about by dropping them into a keyword argument dict, as -with \texttt{**kwargs} above. - -\section{Some hook examples} - -\subsection{Writing meaningful commit messages} - -It's hard to imagine a useful commit message being very short. The -simple \hook{pretxncommit} hook of figure~\ref{ex:hook:msglen.go} -will prevent you from committing a changeset with a message that is -less than ten bytes long. - -\begin{figure}[ht] - \interaction{hook.msglen.go} - \caption{A hook that forbids overly short commit messages} - \label{ex:hook:msglen.go} -\end{figure} - -\subsection{Checking for trailing whitespace} - -An interesting use of a commit-related hook is to help you to write -cleaner code. A simple example of ``cleaner code'' is the dictum that -a change should not add any new lines of text that contain ``trailing -whitespace''. Trailing whitespace is a series of space and tab -characters at the end of a line of text. In most cases, trailing -whitespace is unnecessary, invisible noise, but it is occasionally -problematic, and people often prefer to get rid of it. - -You can use either the \hook{precommit} or \hook{pretxncommit} hook to -tell whether you have a trailing whitespace problem. If you use the -\hook{precommit} hook, the hook will not know which files you are -committing, so it will have to check every modified file in the -repository for trailing white space. If you want to commit a change -to just the file \filename{foo}, but the file \filename{bar} contains -trailing whitespace, doing a check in the \hook{precommit} hook will -prevent you from committing \filename{foo} due to the problem with -\filename{bar}. This doesn't seem right. - -Should you choose the \hook{pretxncommit} hook, the check won't occur -until just before the transaction for the commit completes. This will -allow you to check for problems only the exact files that are being -committed. However, if you entered the commit message interactively -and the hook fails, the transaction will roll back; you'll have to -re-enter the commit message after you fix the trailing whitespace and -run \hgcmd{commit} again. - -\begin{figure}[ht] - \interaction{hook.ws.simple} - \caption{A simple hook that checks for trailing whitespace} - \label{ex:hook:ws.simple} -\end{figure} - -Figure~\ref{ex:hook:ws.simple} introduces a simple \hook{pretxncommit} -hook that checks for trailing whitespace. This hook is short, but not -very helpful. It exits with an error status if a change adds a line -with trailing whitespace to any file, but does not print any -information that might help us to identify the offending file or -line. It also has the nice property of not paying attention to -unmodified lines; only lines that introduce new trailing whitespace -cause problems. - -\begin{figure}[ht] - \interaction{hook.ws.better} - \caption{A better trailing whitespace hook} - \label{ex:hook:ws.better} -\end{figure} - -The example of figure~\ref{ex:hook:ws.better} is much more complex, -but also more useful. It parses a unified diff to see if any lines -add trailing whitespace, and prints the name of the file and the line -number of each such occurrence. Even better, if the change adds -trailing whitespace, this hook saves the commit comment and prints the -name of the save file before exiting and telling Mercurial to roll the -transaction back, so you can use -\hgcmdargs{commit}{\hgopt{commit}{-l}~\emph{filename}} to reuse the -saved commit message once you've corrected the problem. - -As a final aside, note in figure~\ref{ex:hook:ws.better} the use of -\command{perl}'s in-place editing feature to get rid of trailing -whitespace from a file. This is concise and useful enough that I will -reproduce it here. -\begin{codesample2} - perl -pi -e 's,\\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 5981a0f7540a -r 019040fbf5f5 en/htlatex.book --- a/en/htlatex.book Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#!/bin/bash -# -# This script is horrible. It's essentially a hacked copy of -# /usr/bin/htlatex from Fedora Core 6. I apologise for any lasting -# pain reading it causes. - -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -(cd $4 && bibtex hgbook) -(cd $4 && makeindex hgbook) -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -latex $5 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 -echo status $$ diff -r 5981a0f7540a -r 019040fbf5f5 en/intro.tex --- a/en/intro.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,567 +0,0 @@ -\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 5981a0f7540a -r 019040fbf5f5 en/kdiff3.png Binary file en/kdiff3.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/license.tex --- a/en/license.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -\chapter{Open Publication License} -\label{cha:opl} - -Version 1.0, 8 June 1999 - -\section{Requirements on both unmodified and modified versions} - -The Open Publication works may be reproduced and distributed in whole -or in part, in any medium physical or electronic, provided that the -terms of this license are adhered to, and that this license or an -incorporation of it by reference (with any options elected by the -author(s) and/or publisher) is displayed in the reproduction. - -Proper form for an incorporation by reference is as follows: - -\begin{quote} - Copyright (c) \emph{year} by \emph{author's name or designee}. This - material may be distributed only subject to the terms and conditions - set forth in the Open Publication License, v\emph{x.y} or later (the - latest version is presently available at - \url{http://www.opencontent.org/openpub/}). -\end{quote} - -The reference must be immediately followed with any options elected by -the author(s) and/or publisher of the document (see -section~\ref{sec:opl:options}). - -Commercial redistribution of Open Publication-licensed material is -permitted. - -Any publication in standard (paper) book form shall require the -citation of the original publisher and author. The publisher and -author's names shall appear on all outer surfaces of the book. On all -outer surfaces of the book the original publisher's name shall be as -large as the title of the work and cited as possessive with respect to -the title. - -\section{Copyright} - -The copyright to each Open Publication is owned by its author(s) or -designee. - -\section{Scope of license} - -The following license terms apply to all Open Publication works, -unless otherwise explicitly stated in the document. - -Mere aggregation of Open Publication works or a portion of an Open -Publication work with other works or programs on the same media shall -not cause this license to apply to those other works. The aggregate -work shall contain a notice specifying the inclusion of the Open -Publication material and appropriate copyright notice. - -\textbf{Severability}. If any part of this license is found to be -unenforceable in any jurisdiction, the remaining portions of the -license remain in force. - -\textbf{No warranty}. Open Publication works are licensed and provided -``as is'' without warranty of any kind, express or implied, including, -but not limited to, the implied warranties of merchantability and -fitness for a particular purpose or a warranty of non-infringement. - -\section{Requirements on modified works} - -All modified versions of documents covered by this license, including -translations, anthologies, compilations and partial documents, must -meet the following requirements: - -\begin{enumerate} -\item The modified version must be labeled as such. -\item The person making the modifications must be identified and the - modifications dated. -\item Acknowledgement of the original author and publisher if - applicable must be retained according to normal academic citation - practices. -\item The location of the original unmodified document must be - identified. -\item The original author's (or authors') name(s) may not be used to - assert or imply endorsement of the resulting document without the - original author's (or authors') permission. -\end{enumerate} - -\section{Good-practice recommendations} - -In addition to the requirements of this license, it is requested from -and strongly recommended of redistributors that: - -\begin{enumerate} -\item If you are distributing Open Publication works on hardcopy or - CD-ROM, you provide email notification to the authors of your intent - to redistribute at least thirty days before your manuscript or media - freeze, to give the authors time to provide updated documents. This - notification should describe modifications, if any, made to the - document. -\item All substantive modifications (including deletions) be either - clearly marked up in the document or else described in an attachment - to the document. -\item Finally, while it is not mandatory under this license, it is - considered good form to offer a free copy of any hardcopy and CD-ROM - expression of an Open Publication-licensed work to its author(s). -\end{enumerate} - -\section{License options} -\label{sec:opl:options} - -The author(s) and/or publisher of an Open Publication-licensed -document may elect certain options by appending language to the -reference to or copy of the license. These options are considered part -of the license instance and must be included with the license (or its -incorporation by reference) in derived works. - -\begin{enumerate}[A] -\item To prohibit distribution of substantively modified versions - without the explicit permission of the author(s). ``Substantive - modification'' is defined as a change to the semantic content of the - document, and excludes mere changes in format or typographical - corrections. - - To accomplish this, add the phrase ``Distribution of substantively - modified versions of this document is prohibited without the - explicit permission of the copyright holder.'' to the license - reference or copy. - -\item To prohibit any publication of this work or derivative works in - whole or in part in standard (paper) book form for commercial - purposes is prohibited unless prior permission is obtained from the - copyright holder. - - To accomplish this, add the phrase ``Distribution of the work or - derivative of the work in any standard (paper) book form is - prohibited unless prior permission is obtained from the copyright - holder.'' to the license reference or copy. -\end{enumerate} - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/metadata.svg --- a/en/metadata.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,328 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Changelog - Manifest - Filelogs - - diff -r 5981a0f7540a -r 019040fbf5f5 en/mq-collab.tex --- a/en/mq-collab.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,393 +0,0 @@ -\chapter{Advanced uses of Mercurial Queues} -\label{chap:mq-collab} - -While it's easy to pick up straightforward uses of Mercurial Queues, -use of a little discipline and some of MQ's less frequently used -capabilities makes it possible to work in complicated development -environments. - -In this chapter, I will use as an example a technique I have used to -manage the development of an Infiniband device driver for the Linux -kernel. The driver in question is large (at least as drivers go), -with 25,000 lines of code spread across 35 source files. It is -maintained by a small team of developers. - -While much of the material in this chapter is specific to Linux, the -same principles apply to any code base for which you're not the -primary owner, and upon which you need to do a lot of development. - -\section{The problem of many targets} - -The Linux kernel changes rapidly, and has never been internally -stable; developers frequently make drastic changes between releases. -This means that a version of the driver that works well with a -particular released version of the kernel will not even \emph{compile} -correctly against, typically, any other version. - -To maintain a driver, we have to keep a number of distinct versions of -Linux in mind. -\begin{itemize} -\item One target is the main Linux kernel development tree. - Maintenance of the code is in this case partly shared by other - developers in the kernel community, who make ``drive-by'' - modifications to the driver as they develop and refine kernel - subsystems. -\item We also maintain a number of ``backports'' to older versions of - the Linux kernel, to support the needs of customers who are running - older Linux distributions that do not incorporate our drivers. (To - \emph{backport} a piece of code is to modify it to work in an older - version of its target environment than the version it was developed - for.) -\item Finally, we make software releases on a schedule that is - necessarily not aligned with those used by Linux distributors and - kernel developers, so that we can deliver new features to customers - without forcing them to upgrade their entire kernels or - distributions. -\end{itemize} - -\subsection{Tempting approaches that don't work well} - -There are two ``standard'' ways to maintain a piece of software that -has to target many different environments. - -The first is to maintain a number of branches, each intended for a -single target. The trouble with this approach is that you must -maintain iron discipline in the flow of changes between repositories. -A new feature or bug fix must start life in a ``pristine'' repository, -then percolate out to every backport repository. Backport changes are -more limited in the branches they should propagate to; a backport -change that is applied to a branch where it doesn't belong will -probably stop the driver from compiling. - -The second is to maintain a single source tree filled with conditional -statements that turn chunks of code on or off depending on the -intended target. Because these ``ifdefs'' are not allowed in the -Linux kernel tree, a manual or automatic process must be followed to -strip them out and yield a clean tree. A code base maintained in this -fashion rapidly becomes a rat's nest of conditional blocks that are -difficult to understand and maintain. - -Neither of these approaches is well suited to a situation where you -don't ``own'' the canonical copy of a source tree. In the case of a -Linux driver that is distributed with the standard kernel, Linus's -tree contains the copy of the code that will be treated by the world -as canonical. The upstream version of ``my'' driver can be modified -by people I don't know, without me even finding out about it until -after the changes show up in Linus's tree. - -These approaches have the added weakness of making it difficult to -generate well-formed patches to submit upstream. - -In principle, Mercurial Queues seems like a good candidate to manage a -development scenario such as the above. While this is indeed the -case, MQ contains a few added features that make the job more -pleasant. - -\section{Conditionally applying patches with - guards} - -Perhaps the best way to maintain sanity with so many targets is to be -able to choose specific patches to apply for a given situation. MQ -provides a feature called ``guards'' (which originates with quilt's -\texttt{guards} command) that does just this. To start off, let's -create a simple repository for experimenting in. -\interaction{mq.guards.init} -This gives us a tiny repository that contains two patches that don't -have any dependencies on each other, because they touch different files. - -The idea behind conditional application is that you can ``tag'' a -patch with a \emph{guard}, which is simply a text string of your -choosing, then tell MQ to select specific guards to use when applying -patches. MQ will then either apply, or skip over, a guarded patch, -depending on the guards that you have selected. - -A patch can have an arbitrary number of guards; -each one is \emph{positive} (``apply this patch if this guard is -selected'') or \emph{negative} (``skip this patch if this guard is -selected''). A patch with no guards is always applied. - -\section{Controlling the guards on a patch} - -The \hgxcmd{mq}{qguard} command lets you determine which guards should -apply to a patch, or display the guards that are already in effect. -Without any arguments, it displays the guards on the current topmost -patch. -\interaction{mq.guards.qguard} -To set a positive guard on a patch, prefix the name of the guard with -a ``\texttt{+}''. -\interaction{mq.guards.qguard.pos} -To set a negative guard on a patch, prefix the name of the guard with -a ``\texttt{-}''. -\interaction{mq.guards.qguard.neg} - -\begin{note} - The \hgxcmd{mq}{qguard} command \emph{sets} the guards on a patch; it - doesn't \emph{modify} them. What this means is that if you run - \hgcmdargs{qguard}{+a +b} on a patch, then \hgcmdargs{qguard}{+c} on - the same patch, the \emph{only} guard that will be set on it - afterwards is \texttt{+c}. -\end{note} - -Mercurial stores guards in the \sfilename{series} file; the form in -which they are stored is easy both to understand and to edit by hand. -(In other words, you don't have to use the \hgxcmd{mq}{qguard} command if -you don't want to; it's okay to simply edit the \sfilename{series} -file.) -\interaction{mq.guards.series} - -\section{Selecting the guards to use} - -The \hgxcmd{mq}{qselect} command determines which guards are active at a -given time. The effect of this is to determine which patches MQ will -apply the next time you run \hgxcmd{mq}{qpush}. It has no other effect; in -particular, it doesn't do anything to patches that are already -applied. - -With no arguments, the \hgxcmd{mq}{qselect} command lists the guards -currently in effect, one per line of output. Each argument is treated -as the name of a guard to apply. -\interaction{mq.guards.qselect.foo} -In case you're interested, the currently selected guards are stored in -the \sfilename{guards} file. -\interaction{mq.guards.qselect.cat} -We can see the effect the selected guards have when we run -\hgxcmd{mq}{qpush}. -\interaction{mq.guards.qselect.qpush} - -A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}'' -character. The name of a guard must not contain white space, but most -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 5981a0f7540a -r 019040fbf5f5 en/mq-ref.tex --- a/en/mq-ref.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,349 +0,0 @@ -\chapter{Mercurial Queues reference} -\label{chap:mqref} - -\section{MQ command reference} -\label{sec:mqref:cmdref} - -For an overview of the commands provided by MQ, use the command -\hgcmdargs{help}{mq}. - -\subsection{\hgxcmd{mq}{qapplied}---print applied patches} - -The \hgxcmd{mq}{qapplied} command prints the current stack of applied -patches. Patches are printed in oldest-to-newest order, so the last -patch in the list is the ``top'' patch. - -\subsection{\hgxcmd{mq}{qcommit}---commit changes in the queue repository} - -The \hgxcmd{mq}{qcommit} command commits any outstanding changes in the -\sdirname{.hg/patches} repository. This command only works if the -\sdirname{.hg/patches} directory is a repository, i.e.~you created the -directory using \hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} or ran -\hgcmd{init} in the directory after running \hgxcmd{mq}{qinit}. - -This command is shorthand for \hgcmdargs{commit}{--cwd .hg/patches}. - -\subsection{\hgxcmd{mq}{qdelete}---delete a patch from the - \sfilename{series} file} - -The \hgxcmd{mq}{qdelete} command removes the entry for a patch from the -\sfilename{series} file in the \sdirname{.hg/patches} directory. It -does not pop the patch if the patch is already applied. By default, -it does not delete the patch file; use the \hgxopt{mq}{qdel}{-f} option to -do that. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qdel}{-f}] Delete the patch file. -\end{itemize} - -\subsection{\hgxcmd{mq}{qdiff}---print a diff of the topmost applied patch} - -The \hgxcmd{mq}{qdiff} command prints a diff of the topmost applied patch. -It is equivalent to \hgcmdargs{diff}{-r-2:-1}. - -\subsection{\hgxcmd{mq}{qfold}---merge (``fold'') several patches into one} - -The \hgxcmd{mq}{qfold} command merges multiple patches into the topmost -applied patch, so that the topmost applied patch makes the union of -all of the changes in the patches in question. - -The patches to fold must not be applied; \hgxcmd{mq}{qfold} will exit with -an error if any is. The order in which patches are folded is -significant; \hgcmdargs{qfold}{a b} means ``apply the current topmost -patch, followed by \texttt{a}, followed by \texttt{b}''. - -The comments from the folded patches are appended to the comments of -the destination patch, with each block of comments separated by three -asterisk (``\texttt{*}'') characters. Use the \hgxopt{mq}{qfold}{-e} -option to edit the commit message for the combined patch/changeset -after the folding has completed. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qfold}{-e}] Edit the commit message and patch description - for the newly folded patch. -\item[\hgxopt{mq}{qfold}{-l}] Use the contents of the given file as the new - commit message and patch description for the folded patch. -\item[\hgxopt{mq}{qfold}{-m}] Use the given text as the new commit message - and patch description for the folded patch. -\end{itemize} - -\subsection{\hgxcmd{mq}{qheader}---display the header/description of a patch} - -The \hgxcmd{mq}{qheader} command prints the header, or description, of a -patch. By default, it prints the header of the topmost applied patch. -Given an argument, it prints the header of the named patch. - -\subsection{\hgxcmd{mq}{qimport}---import a third-party patch into the queue} - -The \hgxcmd{mq}{qimport} command adds an entry for an external patch to the -\sfilename{series} file, and copies the patch into the -\sdirname{.hg/patches} directory. It adds the entry immediately after -the topmost applied patch, but does not push the patch. - -If the \sdirname{.hg/patches} directory is a repository, -\hgxcmd{mq}{qimport} automatically does an \hgcmd{add} of the imported -patch. - -\subsection{\hgxcmd{mq}{qinit}---prepare a repository to work with MQ} - -The \hgxcmd{mq}{qinit} command prepares a repository to work with MQ. It -creates a directory called \sdirname{.hg/patches}. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qinit}{-c}] Create \sdirname{.hg/patches} as a repository - in its own right. Also creates a \sfilename{.hgignore} file that - will ignore the \sfilename{status} file. -\end{itemize} - -When the \sdirname{.hg/patches} directory is a repository, the -\hgxcmd{mq}{qimport} and \hgxcmd{mq}{qnew} commands automatically \hgcmd{add} -new patches. - -\subsection{\hgxcmd{mq}{qnew}---create a new patch} - -The \hgxcmd{mq}{qnew} command creates a new patch. It takes one mandatory -argument, the name to use for the patch file. The newly created patch -is created empty by default. It is added to the \sfilename{series} -file after the current topmost applied patch, and is immediately -pushed on top of that patch. - -If \hgxcmd{mq}{qnew} finds modified files in the working directory, it will -refuse to create a new patch unless the \hgxopt{mq}{qnew}{-f} option is -used (see below). This behaviour allows you to \hgxcmd{mq}{qrefresh} your -topmost applied patch before you apply a new patch on top of it. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qnew}{-f}] Create a new patch if the contents of the - working directory are modified. Any outstanding modifications are - added to the newly created patch, so after this command completes, - the working directory will no longer be modified. -\item[\hgxopt{mq}{qnew}{-m}] Use the given text as the commit message. - This text will be stored at the beginning of the patch file, before - the patch data. -\end{itemize} - -\subsection{\hgxcmd{mq}{qnext}---print the name of the next patch} - -The \hgxcmd{mq}{qnext} command prints the name name of the next patch in -the \sfilename{series} file after the topmost applied patch. This -patch will become the topmost applied patch if you run \hgxcmd{mq}{qpush}. - -\subsection{\hgxcmd{mq}{qpop}---pop patches off the stack} - -The \hgxcmd{mq}{qpop} command removes applied patches from the top of the -stack of applied patches. By default, it removes only one patch. - -This command removes the changesets that represent the popped patches -from the repository, and updates the working directory to undo the -effects of the patches. - -This command takes an optional argument, which it uses as the name or -index of the patch to pop to. If given a name, it will pop patches -until the named patch is the topmost applied patch. If given a -number, \hgxcmd{mq}{qpop} treats the number as an index into the entries in -the series file, counting from zero (empty lines and lines containing -only comments do not count). It pops patches until the patch -identified by the given index is the topmost applied patch. - -The \hgxcmd{mq}{qpop} command does not read or write patches or the -\sfilename{series} file. It is thus safe to \hgxcmd{mq}{qpop} a patch that -you have removed from the \sfilename{series} file, or a patch that you -have renamed or deleted entirely. In the latter two cases, use the -name of the patch as it was when you applied it. - -By default, the \hgxcmd{mq}{qpop} command will not pop any patches if the -working directory has been modified. You can override this behaviour -using the \hgxopt{mq}{qpop}{-f} option, which reverts all modifications in -the working directory. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qpop}{-a}] Pop all applied patches. This returns the - repository to its state before you applied any patches. -\item[\hgxopt{mq}{qpop}{-f}] Forcibly revert any modifications to the - working directory when popping. -\item[\hgxopt{mq}{qpop}{-n}] Pop a patch from the named queue. -\end{itemize} - -The \hgxcmd{mq}{qpop} command removes one line from the end of the -\sfilename{status} file for each patch that it pops. - -\subsection{\hgxcmd{mq}{qprev}---print the name of the previous patch} - -The \hgxcmd{mq}{qprev} command prints the name of the patch in the -\sfilename{series} file that comes before the topmost applied patch. -This will become the topmost applied patch if you run \hgxcmd{mq}{qpop}. - -\subsection{\hgxcmd{mq}{qpush}---push patches onto the stack} -\label{sec:mqref:cmd:qpush} - -The \hgxcmd{mq}{qpush} command adds patches onto the applied stack. By -default, it adds only one patch. - -This command creates a new changeset to represent each applied patch, -and updates the working directory to apply the effects of the patches. - -The default data used when creating a changeset are as follows: -\begin{itemize} -\item The commit date and time zone are the current date and time - zone. Because these data are used to compute the identity of a - changeset, this means that if you \hgxcmd{mq}{qpop} a patch and - \hgxcmd{mq}{qpush} it again, the changeset that you push will have a - different identity than the changeset you popped. -\item The author is the same as the default used by the \hgcmd{commit} - command. -\item The commit message is any text from the patch file that comes - before the first diff header. If there is no such text, a default - commit message is used that identifies the name of the patch. -\end{itemize} -If a patch contains a Mercurial patch header (XXX add link), the -information in the patch header overrides these defaults. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qpush}{-a}] Push all unapplied patches from the - \sfilename{series} file until there are none left to push. -\item[\hgxopt{mq}{qpush}{-l}] Add the name of the patch to the end - of the commit message. -\item[\hgxopt{mq}{qpush}{-m}] If a patch fails to apply cleanly, use the - entry for the patch in another saved queue to compute the parameters - for a three-way merge, and perform a three-way merge using the - normal Mercurial merge machinery. Use the resolution of the merge - as the new patch content. -\item[\hgxopt{mq}{qpush}{-n}] Use the named queue if merging while pushing. -\end{itemize} - -The \hgxcmd{mq}{qpush} command reads, but does not modify, the -\sfilename{series} file. It appends one line to the \hgcmd{status} -file for each patch that it pushes. - -\subsection{\hgxcmd{mq}{qrefresh}---update the topmost applied patch} - -The \hgxcmd{mq}{qrefresh} command updates the topmost applied patch. It -modifies the patch, removes the old changeset that represented the -patch, and creates a new changeset to represent the modified patch. - -The \hgxcmd{mq}{qrefresh} command looks for the following modifications: -\begin{itemize} -\item Changes to the commit message, i.e.~the text before the first - diff header in the patch file, are reflected in the new changeset - that represents the patch. -\item Modifications to tracked files in the working directory are - added to the patch. -\item Changes to the files tracked using \hgcmd{add}, \hgcmd{copy}, - \hgcmd{remove}, or \hgcmd{rename}. Added files and copy and rename - destinations are added to the patch, while removed files and rename - sources are removed. -\end{itemize} - -Even if \hgxcmd{mq}{qrefresh} detects no changes, it still recreates the -changeset that represents the patch. This causes the identity of the -changeset to differ from the previous changeset that identified the -patch. - -Options: -\begin{itemize} -\item[\hgxopt{mq}{qrefresh}{-e}] Modify the commit and patch description, - using the preferred text editor. -\item[\hgxopt{mq}{qrefresh}{-m}] Modify the commit message and patch - description, using the given text. -\item[\hgxopt{mq}{qrefresh}{-l}] Modify the commit message and patch - description, using text from the given file. -\end{itemize} - -\subsection{\hgxcmd{mq}{qrename}---rename a patch} - -The \hgxcmd{mq}{qrename} command renames a patch, and changes the entry for -the patch in the \sfilename{series} file. - -With a single argument, \hgxcmd{mq}{qrename} renames the topmost applied -patch. With two arguments, it renames its first argument to its -second. - -\subsection{\hgxcmd{mq}{qrestore}---restore saved queue state} - -XXX No idea what this does. - -\subsection{\hgxcmd{mq}{qsave}---save current queue state} - -XXX Likewise. - -\subsection{\hgxcmd{mq}{qseries}---print the entire patch series} - -The \hgxcmd{mq}{qseries} command prints the entire patch series from the -\sfilename{series} file. It prints only patch names, not empty lines -or comments. It prints in order from first to be applied to last. - -\subsection{\hgxcmd{mq}{qtop}---print the name of the current patch} - -The \hgxcmd{mq}{qtop} prints the name of the topmost currently applied -patch. - -\subsection{\hgxcmd{mq}{qunapplied}---print patches not yet applied} - -The \hgxcmd{mq}{qunapplied} command prints the names of patches from the -\sfilename{series} file that are not yet applied. It prints them in -order from the next patch that will be pushed to the last. - -\subsection{\hgcmd{strip}---remove a revision and descendants} - -The \hgcmd{strip} command removes a revision, and all of its -descendants, from the repository. It undoes the effects of the -removed revisions from the repository, and updates the working -directory to the first parent of the removed revision. - -The \hgcmd{strip} command saves a backup of the removed changesets in -a bundle, so that they can be reapplied if removed in error. - -Options: -\begin{itemize} -\item[\hgopt{strip}{-b}] Save unrelated changesets that are intermixed - with the stripped changesets in the backup bundle. -\item[\hgopt{strip}{-f}] If a branch has multiple heads, remove all - heads. XXX This should be renamed, and use \texttt{-f} to strip revs - when there are pending changes. -\item[\hgopt{strip}{-n}] Do not save a backup bundle. -\end{itemize} - -\section{MQ file reference} - -\subsection{The \sfilename{series} file} - -The \sfilename{series} file contains a list of the names of all -patches that MQ can apply. It is represented as a list of names, with -one name saved per line. Leading and trailing white space in each -line are ignored. - -Lines may contain comments. A comment begins with the ``\texttt{\#}'' -character, and extends to the end of the line. Empty lines, and lines -that contain only comments, are ignored. - -You will often need to edit the \sfilename{series} file by hand, hence -the support for comments and empty lines noted above. For example, -you can comment out a patch temporarily, and \hgxcmd{mq}{qpush} will skip -over that patch when applying patches. You can also change the order -in which patches are applied by reordering their entries in the -\sfilename{series} file. - -Placing the \sfilename{series} file under revision control is also -supported; it is a good idea to place all of the patches that it -refers to under revision control, as well. If you create a patch -directory using the \hgxopt{mq}{qinit}{-c} option to \hgxcmd{mq}{qinit}, this -will be done for you automatically. - -\subsection{The \sfilename{status} file} - -The \sfilename{status} file contains the names and changeset hashes of -all patches that MQ currently has applied. Unlike the -\sfilename{series} file, this file is not intended for editing. You -should not place this file under revision control, or modify it in any -way. It is used by MQ strictly for internal book-keeping. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/mq-stack.svg --- a/en/mq-stack.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - prevent-compiler-reorder.patch - - namespace-cleanup.patch - - powerpc-port-fixes.patch - - report-devinfo-correctly.patch - { - { - present in series,but not applied - patches applied,changesets present - topmostapplied patch - 201ad3209902 - 126b84e593ae - a655daf15409 - e50d59aaea3a - - forbid-illegal-params.patch - - fix-memory-leak.patch - - diff -r 5981a0f7540a -r 019040fbf5f5 en/mq.tex --- a/en/mq.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1043 +0,0 @@ -\chapter{Managing change with Mercurial Queues} -\label{chap:mq} - -\section{The patch management problem} -\label{sec:mq:patch-mgmt} - -Here is a common scenario: you need to install a software package from -source, but you find a bug that you must fix in the source before you -can start using the package. You make your changes, forget about the -package for a while, and a few months later you need to upgrade to a -newer version of the package. If the newer version of the package -still has the bug, you must extract your fix from the older source -tree and apply it against the newer version. This is a tedious task, -and it's easy to make mistakes. - -This is a simple case of the ``patch management'' problem. You have -an ``upstream'' source tree that you can't change; you need to make -some local changes on top of the upstream tree; and you'd like to be -able to keep those changes separate, so that you can apply them to -newer versions of the upstream source. - -The patch management problem arises in many situations. Probably the -most visible is that a user of an open source software project will -contribute a bug fix or new feature to the project's maintainers in the -form of a patch. - -Distributors of operating systems that include open source software -often need to make changes to the packages they distribute so that -they will build properly in their environments. - -When you have few changes to maintain, it is easy to manage a single -patch using the standard \command{diff} and \command{patch} programs -(see section~\ref{sec:mq:patch} for a discussion of these tools). -Once the number of changes grows, it starts to make sense to maintain -patches as discrete ``chunks of work,'' so that for example a single -patch will contain only one bug fix (the patch might modify several -files, but it's doing ``only one thing''), and you may have a number -of such patches for different bugs you need fixed and local changes -you require. In this situation, if you submit a bug fix patch to the -upstream maintainers of a package and they include your fix in a -subsequent release, you can simply drop that single patch when you're -updating to the newer release. - -Maintaining a single patch against an upstream tree is a little -tedious and error-prone, but not difficult. However, the complexity -of the problem grows rapidly as the number of patches you have to -maintain increases. With more than a tiny number of patches in hand, -understanding which ones you have applied and maintaining them moves -from messy to overwhelming. - -Fortunately, Mercurial includes a powerful extension, Mercurial Queues -(or simply ``MQ''), that massively simplifies the patch management -problem. - -\section{The prehistory of Mercurial Queues} -\label{sec:mq:history} - -During the late 1990s, several Linux kernel developers started to -maintain ``patch series'' that modified the behaviour of the Linux -kernel. Some of these series were focused on stability, some on -feature coverage, and others were more speculative. - -The sizes of these patch series grew rapidly. In 2002, Andrew Morton -published some shell scripts he had been using to automate the task of -managing his patch queues. Andrew was successfully using these -scripts to manage hundreds (sometimes thousands) of patches on top of -the Linux kernel. - -\subsection{A patchwork quilt} -\label{sec:mq:quilt} - -In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the -approach of Andrew's scripts and published a tool called ``patchwork -quilt''~\cite{web:quilt}, or simply ``quilt'' -(see~\cite{gruenbacher:2005} for a paper describing it). Because -quilt substantially automated patch management, it rapidly gained a -large following among open source software developers. - -Quilt manages a \emph{stack of patches} on top of a directory tree. -To begin, you tell quilt to manage a directory tree, and tell it which -files you want to manage; it stores away the names and contents of -those files. To fix a bug, you create a new patch (using a single -command), edit the files you need to fix, then ``refresh'' the patch. - -The refresh step causes quilt to scan the directory tree; it updates -the patch with all of the changes you have made. You can create -another patch on top of the first, which will track the changes -required to modify the tree from ``tree with one patch applied'' to -``tree with two patches applied''. - -You can \emph{change} which patches are applied to the tree. If you -``pop'' a patch, the changes made by that patch will vanish from the -directory tree. Quilt remembers which patches you have popped, -though, so you can ``push'' a popped patch again, and the directory -tree will be restored to contain the modifications in the patch. Most -importantly, you can run the ``refresh'' command at any time, and the -topmost applied patch will be updated. This means that you can, at -any time, change both which patches are applied and what -modifications those patches make. - -Quilt knows nothing about revision control tools, so it works equally -well on top of an unpacked tarball or a Subversion working copy. - -\subsection{From patchwork quilt to Mercurial Queues} -\label{sec:mq:quilt-mq} - -In mid-2005, Chris Mason took the features of quilt and wrote an -extension that he called Mercurial Queues, which added quilt-like -behaviour to Mercurial. - -The key difference between quilt and MQ is that quilt knows nothing -about revision control systems, while MQ is \emph{integrated} into -Mercurial. Each patch that you push is represented as a Mercurial -changeset. Pop a patch, and the changeset goes away. - -Because quilt does not care about revision control tools, it is still -a tremendously useful piece of software to know about for situations -where you cannot use Mercurial and MQ. - -\section{The huge advantage of MQ} - -I cannot overstate the value that MQ offers through the unification of -patches and revision control. - -A major reason that patches have persisted in the free software and -open source world---in spite of the availability of increasingly -capable revision control tools over the years---is the \emph{agility} -they offer. - -Traditional revision control tools make a permanent, irreversible -record of everything that you do. While this has great value, it's -also somewhat stifling. If you want to perform a wild-eyed -experiment, you have to be careful in how you go about it, or you risk -leaving unneeded---or worse, misleading or destabilising---traces of -your missteps and errors in the permanent revision record. - -By contrast, MQ's marriage of distributed revision control with -patches makes it much easier to isolate your work. Your patches live -on top of normal revision history, and you can make them disappear or -reappear at will. If you don't like a patch, you can drop it. If a -patch isn't quite as you want it to be, simply fix it---as many times -as you need to, until you have refined it into the form you desire. - -As an example, the integration of patches with revision control makes -understanding patches and debugging their effects---and their -interplay with the code they're based on---\emph{enormously} easier. -Since every applied patch has an associated changeset, you can use -\hgcmdargs{log}{\emph{filename}} to see which changesets and patches -affected a file. You can use the \hgext{bisect} command to -binary-search through all changesets and applied patches to see where -a bug got introduced or fixed. You can use the \hgcmd{annotate} -command to see which changeset or patch modified a particular line of -a source file. And so on. - -\section{Understanding patches} -\label{sec:mq:patch} - -Because MQ doesn't hide its patch-oriented nature, it is helpful to -understand what patches are, and a little about the tools that work -with them. - -The traditional Unix \command{diff} command compares two files, and -prints a list of differences between them. The \command{patch} command -understands these differences as \emph{modifications} to make to a -file. Take a look at figure~\ref{ex:mq:diff} for a simple example of -these commands in action. - -\begin{figure}[ht] - \interaction{mq.dodiff.diff} - \caption{Simple uses of the \command{diff} and \command{patch} commands} - \label{ex:mq:diff} -\end{figure} - -The type of file that \command{diff} generates (and \command{patch} -takes as input) is called a ``patch'' or a ``diff''; there is no -difference between a patch and a diff. (We'll use the term ``patch'', -since it's more commonly used.) - -A patch file can start with arbitrary text; the \command{patch} -command ignores this text, but MQ uses it as the commit message when -creating changesets. To find the beginning of the patch content, -\command{patch} searches for the first line that starts with the -string ``\texttt{diff~-}''. - -MQ works with \emph{unified} diffs (\command{patch} can accept several -other diff formats, but MQ doesn't). A unified diff contains two -kinds of header. The \emph{file header} describes the file being -modified; it contains the name of the file to modify. When -\command{patch} sees a new file header, it looks for a file with that -name to start modifying. - -After the file header comes a series of \emph{hunks}. Each hunk -starts with a header; this identifies the range of line numbers within -the file that the hunk should modify. Following the header, a hunk -starts and ends with a few (usually three) lines of text from the -unmodified file; these are called the \emph{context} for the hunk. If -there's only a small amount of context between successive hunks, -\command{diff} doesn't print a new hunk header; it just runs the hunks -together, with a few lines of context between modifications. - -Each line of context begins with a space character. Within the hunk, -a line that begins with ``\texttt{-}'' means ``remove this line,'' -while a line that begins with ``\texttt{+}'' means ``insert this -line.'' For example, a line that is modified is represented by one -deletion and one insertion. - -We will return to some of the more subtle aspects of patches later (in -section~\ref{sec:mq:adv-patch}), but you should have enough information -now to use MQ. - -\section{Getting started with Mercurial Queues} -\label{sec:mq:start} - -Because MQ is implemented as an extension, you must explicitly enable -before you can use it. (You don't need to download anything; MQ ships -with the standard Mercurial distribution.) To enable MQ, edit your -\tildefile{.hgrc} file, and add the lines in figure~\ref{ex:mq:config}. - -\begin{figure}[ht] - \begin{codesample4} - [extensions] - hgext.mq = - \end{codesample4} - \label{ex:mq:config} - \caption{Contents to add to \tildefile{.hgrc} to enable the MQ extension} -\end{figure} - -Once the extension is enabled, it will make a number of new commands -available. To verify that the extension is working, you can use -\hgcmd{help} to see if the \hgxcmd{mq}{qinit} command is now available; see -the example in figure~\ref{ex:mq:enabled}. - -\begin{figure}[ht] - \interaction{mq.qinit-help.help} - \caption{How to verify that MQ is enabled} - \label{ex:mq:enabled} -\end{figure} - -You can use MQ with \emph{any} Mercurial repository, and its commands -only operate within that repository. To get started, simply prepare -the repository using the \hgxcmd{mq}{qinit} command (see -figure~\ref{ex:mq:qinit}). This command creates an empty directory -called \sdirname{.hg/patches}, where MQ will keep its metadata. As -with many Mercurial commands, the \hgxcmd{mq}{qinit} command prints nothing -if it succeeds. - -\begin{figure}[ht] - \interaction{mq.tutorial.qinit} - \caption{Preparing a repository for use with MQ} - \label{ex:mq:qinit} -\end{figure} - -\begin{figure}[ht] - \interaction{mq.tutorial.qnew} - \caption{Creating a new patch} - \label{ex:mq:qnew} -\end{figure} - -\subsection{Creating a new patch} - -To begin work on a new patch, use the \hgxcmd{mq}{qnew} command. This -command takes one argument, the name of the patch to create. MQ will -use this as the name of an actual file in the \sdirname{.hg/patches} -directory, as you can see in figure~\ref{ex:mq:qnew}. - -Also newly present in the \sdirname{.hg/patches} directory are two -other files, \sfilename{series} and \sfilename{status}. The -\sfilename{series} file lists all of the patches that MQ knows about -for this repository, with one patch per line. Mercurial uses the -\sfilename{status} file for internal book-keeping; it tracks all of the -patches that MQ has \emph{applied} in this repository. - -\begin{note} - You may sometimes want to edit the \sfilename{series} file by hand; - for example, to change the sequence in which some patches are - applied. However, manually editing the \sfilename{status} file is - almost always a bad idea, as it's easy to corrupt MQ's idea of what - is happening. -\end{note} - -Once you have created your new patch, you can edit files in the -working directory as you usually would. All of the normal Mercurial -commands, such as \hgcmd{diff} and \hgcmd{annotate}, work exactly as -they did before. - -\subsection{Refreshing a patch} - -When you reach a point where you want to save your work, use the -\hgxcmd{mq}{qrefresh} command (figure~\ref{ex:mq:qnew}) to update the patch -you are working on. This command folds the changes you have made in -the working directory into your patch, and updates its corresponding -changeset to contain those changes. - -\begin{figure}[ht] - \interaction{mq.tutorial.qrefresh} - \caption{Refreshing a patch} - \label{ex:mq:qrefresh} -\end{figure} - -You can run \hgxcmd{mq}{qrefresh} as often as you like, so it's a good way -to ``checkpoint'' your work. Refresh your patch at an opportune -time; try an experiment; and if the experiment doesn't work out, -\hgcmd{revert} your modifications back to the last time you refreshed. - -\begin{figure}[ht] - \interaction{mq.tutorial.qrefresh2} - \caption{Refresh a patch many times to accumulate changes} - \label{ex:mq:qrefresh2} -\end{figure} - -\subsection{Stacking and tracking patches} - -Once you have finished working on a patch, or need to work on another, -you can use the \hgxcmd{mq}{qnew} command again to create a new patch. -Mercurial will apply this patch on top of your existing patch. See -figure~\ref{ex:mq:qnew2} for an example. Notice that the patch -contains the changes in our prior patch as part of its context (you -can see this more clearly in the output of \hgcmd{annotate}). - -\begin{figure}[ht] - \interaction{mq.tutorial.qnew2} - \caption{Stacking a second patch on top of the first} - \label{ex:mq:qnew2} -\end{figure} - -So far, with the exception of \hgxcmd{mq}{qnew} and \hgxcmd{mq}{qrefresh}, we've -been careful to only use regular Mercurial commands. However, MQ -provides many commands that are easier to use when you are thinking -about patches, as illustrated in figure~\ref{ex:mq:qseries}: - -\begin{itemize} -\item The \hgxcmd{mq}{qseries} command lists every patch that MQ knows - about in this repository, from oldest to newest (most recently - \emph{created}). -\item The \hgxcmd{mq}{qapplied} command lists every patch that MQ has - \emph{applied} in this repository, again from oldest to newest (most - recently applied). -\end{itemize} - -\begin{figure}[ht] - \interaction{mq.tutorial.qseries} - \caption{Understanding the patch stack with \hgxcmd{mq}{qseries} and - \hgxcmd{mq}{qapplied}} - \label{ex:mq:qseries} -\end{figure} - -\subsection{Manipulating the patch stack} - -The previous discussion implied that there must be a difference -between ``known'' and ``applied'' patches, and there is. MQ can -manage a patch without it being applied in the repository. - -An \emph{applied} patch has a corresponding changeset in the -repository, and the effects of the patch and changeset are visible in -the working directory. You can undo the application of a patch using -the \hgxcmd{mq}{qpop} command. MQ still \emph{knows about}, or manages, a -popped patch, but the patch no longer has a corresponding changeset in -the repository, and the working directory does not contain the changes -made by the patch. Figure~\ref{fig:mq:stack} illustrates the -difference between applied and tracked patches. - -\begin{figure}[ht] - \centering - \grafix{mq-stack} - \caption{Applied and unapplied patches in the MQ patch stack} - \label{fig:mq:stack} -\end{figure} - -You can reapply an unapplied, or popped, patch using the \hgxcmd{mq}{qpush} -command. This creates a new changeset to correspond to the patch, and -the patch's changes once again become present in the working -directory. See figure~\ref{ex:mq:qpop} for examples of \hgxcmd{mq}{qpop} -and \hgxcmd{mq}{qpush} in action. Notice that once we have popped a patch -or two patches, the output of \hgxcmd{mq}{qseries} remains the same, while -that of \hgxcmd{mq}{qapplied} has changed. - -\begin{figure}[ht] - \interaction{mq.tutorial.qpop} - \caption{Modifying the stack of applied patches} - \label{ex:mq:qpop} -\end{figure} - -\subsection{Pushing and popping many patches} - -While \hgxcmd{mq}{qpush} and \hgxcmd{mq}{qpop} each operate on a single patch at -a time by default, you can push and pop many patches in one go. The -\hgxopt{mq}{qpush}{-a} option to \hgxcmd{mq}{qpush} causes it to push all -unapplied patches, while the \hgxopt{mq}{qpop}{-a} option to \hgxcmd{mq}{qpop} -causes it to pop all applied patches. (For some more ways to push and -pop many patches, see section~\ref{sec:mq:perf} below.) - -\begin{figure}[ht] - \interaction{mq.tutorial.qpush-a} - \caption{Pushing all unapplied patches} - \label{ex:mq:qpush-a} -\end{figure} - -\subsection{Safety checks, and overriding them} - -Several MQ commands check the working directory before they do -anything, and fail if they find any modifications. They do this to -ensure that you won't lose any changes that you have made, but not yet -incorporated into a patch. Figure~\ref{ex:mq:add} illustrates this; -the \hgxcmd{mq}{qnew} command will not create a new patch if there are -outstanding changes, caused in this case by the \hgcmd{add} of -\filename{file3}. - -\begin{figure}[ht] - \interaction{mq.tutorial.add} - \caption{Forcibly creating a patch} - \label{ex:mq:add} -\end{figure} - -Commands that check the working directory all take an ``I know what -I'm doing'' option, which is always named \option{-f}. The exact -meaning of \option{-f} depends on the command. For example, -\hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} will incorporate any outstanding -changes into the new patch it creates, but -\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} will revert modifications to any -files affected by the patch that it is popping. Be sure to read the -documentation for a command's \option{-f} option before you use it! - -\subsection{Working on several patches at once} - -The \hgxcmd{mq}{qrefresh} command always refreshes the \emph{topmost} -applied patch. This means that you can suspend work on one patch (by -refreshing it), pop or push to make a different patch the top, and -work on \emph{that} patch for a while. - -Here's an example that illustrates how you can use this ability. -Let's say you're developing a new feature as two patches. The first -is a change to the core of your software, and the second---layered on -top of the first---changes the user interface to use the code you just -added to the core. If you notice a bug in the core while you're -working on the UI patch, it's easy to fix the core. Simply -\hgxcmd{mq}{qrefresh} the UI patch to save your in-progress changes, and -\hgxcmd{mq}{qpop} down to the core patch. Fix the core bug, -\hgxcmd{mq}{qrefresh} the core patch, and \hgxcmd{mq}{qpush} back to the UI -patch to continue where you left off. - -\section{More about patches} -\label{sec:mq:adv-patch} - -MQ uses the GNU \command{patch} command to apply patches, so it's -helpful to know a few more detailed aspects of how \command{patch} -works, and about patches themselves. - -\subsection{The strip count} - -If you look at the file headers in a patch, you will notice that the -pathnames usually have an extra component on the front that isn't -present in the actual path name. This is a holdover from the way that -people used to generate patches (people still do this, but it's -somewhat rare with modern revision control tools). - -Alice would unpack a tarball, edit her files, then decide that she -wanted to create a patch. So she'd rename her working directory, -unpack the tarball again (hence the need for the rename), and use the -\cmdopt{diff}{-r} and \cmdopt{diff}{-N} options to \command{diff} to -recursively generate a patch between the unmodified directory and the -modified one. The result would be that the name of the unmodified -directory would be at the front of the left-hand path in every file -header, and the name of the modified directory would be at the front -of the right-hand path. - -Since someone receiving a patch from the Alices of the net would be -unlikely to have unmodified and modified directories with exactly the -same names, the \command{patch} command has a \cmdopt{patch}{-p} -option that indicates the number of leading path name components to -strip when trying to apply a patch. This number is called the -\emph{strip count}. - -An option of ``\texttt{-p1}'' means ``use a strip count of one''. If -\command{patch} sees a file name \filename{foo/bar/baz} in a file -header, it will strip \filename{foo} and try to patch a file named -\filename{bar/baz}. (Strictly speaking, the strip count refers to the -number of \emph{path separators} (and the components that go with them -) to strip. A strip count of one will turn \filename{foo/bar} into -\filename{bar}, but \filename{/foo/bar} (notice the extra leading -slash) into \filename{foo/bar}.) - -The ``standard'' strip count for patches is one; almost all patches -contain one leading path name component that needs to be stripped. -Mercurial's \hgcmd{diff} command generates path names in this form, -and the \hgcmd{import} command and MQ expect patches to have a strip -count of one. - -If you receive a patch from someone that you want to add to your patch -queue, and the patch needs a strip count other than one, you cannot -just \hgxcmd{mq}{qimport} the patch, because \hgxcmd{mq}{qimport} does not yet -have a \texttt{-p} option (see~\bug{311}). Your best bet is to -\hgxcmd{mq}{qnew} a patch of your own, then use \cmdargs{patch}{-p\emph{N}} -to apply their patch, followed by \hgcmd{addremove} to pick up any -files added or removed by the patch, followed by \hgxcmd{mq}{qrefresh}. -This complexity may become unnecessary; see~\bug{311} for details. -\subsection{Strategies for applying a patch} - -When \command{patch} applies a hunk, it tries a handful of -successively less accurate strategies to try to make the hunk apply. -This falling-back technique often makes it possible to take a patch -that was generated against an old version of a file, and apply it -against a newer version of that file. - -First, \command{patch} tries an exact match, where the line numbers, -the context, and the text to be modified must apply exactly. If it -cannot make an exact match, it tries to find an exact match for the -context, without honouring the line numbering information. If this -succeeds, it prints a line of output saying that the hunk was applied, -but at some \emph{offset} from the original line number. - -If a context-only match fails, \command{patch} removes the first and -last lines of the context, and tries a \emph{reduced} context-only -match. If the hunk with reduced context succeeds, it prints a message -saying that it applied the hunk with a \emph{fuzz factor} (the number -after the fuzz factor indicates how many lines of context -\command{patch} had to trim before the patch applied). - -When neither of these techniques works, \command{patch} prints a -message saying that the hunk in question was rejected. It saves -rejected hunks (also simply called ``rejects'') to a file with the -same name, and an added \sfilename{.rej} extension. It also saves an -unmodified copy of the file with a \sfilename{.orig} extension; the -copy of the file without any extensions will contain any changes made -by hunks that \emph{did} apply cleanly. If you have a patch that -modifies \filename{foo} with six hunks, and one of them fails to -apply, you will have: an unmodified \filename{foo.orig}, a -\filename{foo.rej} containing one hunk, and \filename{foo}, containing -the changes made by the five successful 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 5981a0f7540a -r 019040fbf5f5 en/note.png Binary file en/note.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 en/preface.tex --- a/en/preface.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -\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 5981a0f7540a -r 019040fbf5f5 en/revlog.svg --- a/en/revlog.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - Second parent - 32bf9a5f22c0 - - - - Revision hash - 34b8b7a15ea1 - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - 000000000000 - - - - Second parent - 000000000000 - - - - - Revision hash - ff9dc8bc2a8b - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - 34b8b7a15ea1 - - - - Second parent - 000000000000 - - - - Revision hash - 1b67dc96f27a - - - - ... - Revision data (delta or snapshot) - - - - - - - - First parent - ff9dc8bc2a8b - - - - Second parent - 000000000000 - - - - Revision hash - 5b80c922ebdd - - - - ... - Revision data (delta or snapshot) - - - - - - - First parent - ecacb6b4c9fd - - - - Second parent - 000000000000 - - - - Revision hash - 32bf9a5f22c0 - - - - ... - Revision data (delta or snapshot) - - - - - - First parent - ff9dc8bc2a8b - - - - Second parent - 000000000000 - - - - Revision hash - ecacb6b4c9fd - - - - ... - Revision data (delta or snapshot) - - - - - - - Head revision(no children) - Merge revision(two parents) - Branches(two revisions,same parent) - - - First revision(both parents null) - - First parent - 5b80c922ebdd - - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/snapshot.svg --- a/en/snapshot.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - - - - Index, rev 7 - - Revlog index (.i file) - Revlog data (.d file) - - - Snapshot, rev 4 - - Delta, rev 4 to 5 - - Delta, rev 5 to 6 - - Delta, rev 6 to 7 - - Delta, rev 2 to 3 - - diff -r 5981a0f7540a -r 019040fbf5f5 en/srcinstall.tex --- a/en/srcinstall.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -\chapter{Installing Mercurial from source} -\label{chap:srcinstall} - -\section{On a Unix-like system} -\label{sec:srcinstall:unixlike} - -If you are using a Unix-like system that has a sufficiently recent -version of Python (2.3~or newer) available, it is easy to install -Mercurial from source. -\begin{enumerate} -\item Download a recent source tarball from - \url{http://www.selenic.com/mercurial/download}. -\item Unpack the tarball: - \begin{codesample4} - gzip -dc mercurial-\emph{version}.tar.gz | tar xf - - \end{codesample4} -\item Go into the source directory and run the installer script. This - will build Mercurial and install it in your home directory. - \begin{codesample4} - cd mercurial-\emph{version} - python setup.py install --force --home=\$HOME - \end{codesample4} -\end{enumerate} -Once the install finishes, Mercurial will be in the \texttt{bin} -subdirectory of your home directory. Don't forget to make sure that -this directory is present in your shell's search path. - -You will probably need to set the \envar{PYTHONPATH} environment -variable so that the Mercurial executable can find the rest of the -Mercurial packages. For example, on my laptop, I have set it to -\texttt{/home/bos/lib/python}. The exact path that you will need to -use depends on how Python was built for your system, but should be -easy to figure out. If you're uncertain, look through the output of -the installer script above, and see where the contents of the -\texttt{mercurial} directory were installed to. - -\section{On Windows} - -Building and installing Mercurial on Windows requires a variety of -tools, a fair amount of technical knowledge, and considerable -patience. I very much \emph{do not recommend} this route if you are a -``casual user''. Unless you intend to hack on Mercurial, I strongly -suggest that you use a binary package instead. - -If you are intent on building Mercurial from source on Windows, follow -the ``hard way'' directions on the Mercurial wiki at -\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall}, -and expect the process to involve a lot of fiddly work. - -%%% Local Variables: -%%% mode: latex -%%% TeX-master: "00book" -%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 en/template.tex --- a/en/template.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,475 +0,0 @@ -\chapter{Customising the output of Mercurial} -\label{chap:template} - -Mercurial provides a powerful mechanism to let you control how it -displays information. The mechanism is based on templates. You can -use templates to generate specific output for a single command, or to -customise the entire appearance of the built-in web interface. - -\section{Using precanned output styles} -\label{sec:style} - -Packaged with Mercurial are some output styles that you can use -immediately. A style is simply a precanned template that someone -wrote and installed somewhere that Mercurial can find. - -Before we take a look at Mercurial's bundled styles, let's review its -normal output. - -\interaction{template.simple.normal} - -This is somewhat informative, but it takes up a lot of space---five -lines of output per changeset. The \texttt{compact} style reduces -this to three lines, presented in a sparse manner. - -\interaction{template.simple.compact} - -The \texttt{changelog} style hints at the expressive power of -Mercurial's templating engine. This style attempts to follow the GNU -Project's changelog guidelines\cite{web:changelog}. - -\interaction{template.simple.changelog} - -You will not be shocked to learn that Mercurial's default output style -is named \texttt{default}. - -\subsection{Setting a default style} - -You can modify the output style that Mercurial will use for every -command by editing your \hgrc\ file, naming the style you would -prefer to use. - -\begin{codesample2} - [ui] - style = compact -\end{codesample2} - -If you write a style of your own, you can use it by either providing -the path to your style file, or copying your style file into a -location where Mercurial can find it (typically the \texttt{templates} -subdirectory of your Mercurial install directory). - -\section{Commands that support styles and templates} - -All of Mercurial's ``\texttt{log}-like'' commands let you use styles -and templates: \hgcmd{incoming}, \hgcmd{log}, \hgcmd{outgoing}, and -\hgcmd{tip}. - -As I write this manual, these are so far the only commands that -support styles and templates. Since these are the most important -commands that need customisable output, there has been little pressure -from the Mercurial user community to add style and template support to -other commands. - -\section{The basics of templating} - -At its simplest, a Mercurial template is a piece of text. Some of the -text never changes, while other parts are \emph{expanded}, or replaced -with new text, when necessary. - -Before we continue, let's look again at a simple example of -Mercurial's normal output. - -\interaction{template.simple.normal} - -Now, let's run the same command, but using a template to change its -output. - -\interaction{template.simple.simplest} - -The example above illustrates the simplest possible template; it's -just a piece of static text, printed once for each changeset. The -\hgopt{log}{--template} option to the \hgcmd{log} command tells -Mercurial to use the given text as the template when printing each -changeset. - -Notice that the template string above ends with the text -``\Verb+\n+''. This is an \emph{escape sequence}, telling Mercurial -to print a newline at the end of each template item. If you omit this -newline, Mercurial will run each piece of output together. See -section~\ref{sec:template:escape} for more details of escape sequences. - -A template that prints a fixed string of text all the time isn't very -useful; let's try something a bit more complex. - -\interaction{template.simple.simplesub} - -As you can see, the string ``\Verb+{desc}+'' in the template has been -replaced in the output with the description of each changeset. Every -time Mercurial finds text enclosed in curly braces (``\texttt{\{}'' -and ``\texttt{\}}''), it will try to replace the braces and text with -the expansion of whatever is inside. To print a literal curly brace, -you must escape it, as described in section~\ref{sec:template:escape}. - -\section{Common template keywords} -\label{sec:template:keyword} - -You can start writing simple templates immediately using the keywords -below. - -\begin{itemize} -\item[\tplkword{author}] String. The unmodified author of the changeset. -\item[\tplkword{branches}] String. The name of the branch on which - the changeset was committed. Will be empty if the branch name was - \texttt{default}. -\item[\tplkword{date}] Date information. The date when the changeset - was committed. This is \emph{not} human-readable; you must pass it - through a filter that will render it appropriately. See - section~\ref{sec:template:filter} for more information on filters. - The date is expressed as a pair of numbers. The first number is a - Unix UTC timestamp (seconds since January 1, 1970); the second is - the offset of the committer's timezone from UTC, in seconds. -\item[\tplkword{desc}] String. The text of the changeset description. -\item[\tplkword{files}] List of strings. All files modified, added, or - removed by this changeset. -\item[\tplkword{file\_adds}] List of strings. Files added by this - changeset. -\item[\tplkword{file\_dels}] List of strings. Files removed by this - changeset. -\item[\tplkword{node}] String. The changeset identification hash, as a - 40-character hexadecimal string. -\item[\tplkword{parents}] List of strings. The parents of the - changeset. -\item[\tplkword{rev}] Integer. The repository-local changeset revision - number. -\item[\tplkword{tags}] List of strings. Any tags associated with the - changeset. -\end{itemize} - -A few simple experiments will show us what to expect when we use these -keywords; you can see the results in -figure~\ref{fig:template:keywords}. - -\begin{figure} - \interaction{template.simple.keywords} - \caption{Template keywords in use} - \label{fig:template:keywords} -\end{figure} - -As we noted above, the date keyword does not produce human-readable -output, so we must treat it specially. This involves using a -\emph{filter}, about which more in section~\ref{sec:template:filter}. - -\interaction{template.simple.datekeyword} - -\section{Escape sequences} -\label{sec:template:escape} - -Mercurial's templating engine recognises the most commonly used escape -sequences in strings. When it sees a backslash (``\Verb+\+'') -character, it looks at the following character and substitutes the two -characters with a single replacement, as described below. - -\begin{itemize} -\item[\Verb+\textbackslash\textbackslash+] Backslash, ``\Verb+\+'', - ASCII~134. -\item[\Verb+\textbackslash n+] Newline, ASCII~12. -\item[\Verb+\textbackslash r+] Carriage return, ASCII~15. -\item[\Verb+\textbackslash t+] Tab, ASCII~11. -\item[\Verb+\textbackslash v+] Vertical tab, ASCII~13. -\item[\Verb+\textbackslash \{+] Open curly brace, ``\Verb+{+'', ASCII~173. -\item[\Verb+\textbackslash \}+] Close curly brace, ``\Verb+}+'', ASCII~175. -\end{itemize} - -As indicated above, if you want the expansion of a template to contain -a literal ``\Verb+\+'', ``\Verb+{+'', or ``\Verb+{+'' character, you -must escape it. - -\section{Filtering keywords to change their results} -\label{sec:template:filter} - -Some of the results of template expansion are not immediately easy to -use. Mercurial lets you specify an optional chain of \emph{filters} -to modify the result of expanding a keyword. You have already seen a -common filter, \tplkwfilt{date}{isodate}, in action above, to make a -date readable. - -Below is a list of the most commonly used filters that Mercurial -supports. While some filters can be applied to any text, others can -only be used in specific circumstances. The name of each filter is -followed first by an indication of where it can be used, then a -description of its effect. - -\begin{itemize} -\item[\tplfilter{addbreaks}] Any text. Add an XHTML ``\Verb+
    +'' - tag before the end of every line except the last. For example, - ``\Verb+foo\nbar+'' becomes ``\Verb+foo
    \nbar+''. -\item[\tplkwfilt{date}{age}] \tplkword{date} keyword. Render the - age of the date, relative to the current time. Yields a string like - ``\Verb+10 minutes+''. -\item[\tplfilter{basename}] Any text, but most useful for the - \tplkword{files} keyword and its relatives. Treat the text as a - path, and return the basename. For example, ``\Verb+foo/bar/baz+'' - becomes ``\Verb+baz+''. -\item[\tplkwfilt{date}{date}] \tplkword{date} keyword. Render a date - in a similar format to the Unix \tplkword{date} command, but with - timezone included. Yields a string like - ``\Verb+Mon Sep 04 15:13:13 2006 -0700+''. -\item[\tplkwfilt{author}{domain}] Any text, but most useful for the - \tplkword{author} keyword. Finds the first string that looks like - an email address, and extract just the domain component. For - example, ``\Verb+Bryan O'Sullivan +'' becomes - ``\Verb+serpentine.com+''. -\item[\tplkwfilt{author}{email}] Any text, but most useful for the - \tplkword{author} keyword. Extract the first string that looks like - an email address. For example, - ``\Verb+Bryan O'Sullivan +'' becomes - ``\Verb+bos@serpentine.com+''. -\item[\tplfilter{escape}] Any text. Replace the special XML/XHTML - characters ``\Verb+&+'', ``\Verb+<+'' and ``\Verb+>+'' with - XML entities. -\item[\tplfilter{fill68}] Any text. Wrap the text to fit in 68 - columns. This is useful before you pass text through the - \tplfilter{tabindent} filter, and still want it to fit in an - 80-column fixed-font window. -\item[\tplfilter{fill76}] Any text. Wrap the text to fit in 76 - columns. -\item[\tplfilter{firstline}] Any text. Yield the first line of text, - without any trailing newlines. -\item[\tplkwfilt{date}{hgdate}] \tplkword{date} keyword. Render the - date as a pair of readable numbers. Yields a string like - ``\Verb+1157407993 25200+''. -\item[\tplkwfilt{date}{isodate}] \tplkword{date} keyword. Render the - date as a text string in ISO~8601 format. Yields a string like - ``\Verb+2006-09-04 15:13:13 -0700+''. -\item[\tplfilter{obfuscate}] Any text, but most useful for the - \tplkword{author} keyword. Yield the input text rendered as a - sequence of XML entities. This helps to defeat some particularly - stupid screen-scraping email harvesting spambots. -\item[\tplkwfilt{author}{person}] Any text, but most useful for the - \tplkword{author} keyword. Yield the text before an email address. - For example, ``\Verb+Bryan O'Sullivan +'' - becomes ``\Verb+Bryan O'Sullivan+''. -\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} keyword. Render a - date using the same format used in email headers. Yields a string - like ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+''. -\item[\tplkwfilt{node}{short}] Changeset hash. Yield the short form - of a changeset hash, i.e.~a 12-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 5981a0f7540a -r 019040fbf5f5 en/tour-basic.tex --- a/en/tour-basic.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,617 +0,0 @@ -\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 5981a0f7540a -r 019040fbf5f5 en/tour-history.svg --- a/en/tour-history.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - 0: 0a04 - - 1: 82e5 - - 2: 057d - - 3: ff5d - - 4: b57f - - - - - - (newest) - (oldest) - - 4: b57f - - - revisionnumber - changesetidentifier - - diff -r 5981a0f7540a -r 019040fbf5f5 en/tour-merge-conflict.svg --- a/en/tour-merge-conflict.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - Greetings!I am Mariam Abacha, the wife of former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - - - - Greetings!I am Shehu Musa Abacha, cousin to the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - - - - Greetings!I am Alhaji Abba Abacha, son of the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing - - - Base version - Our changes - Their changes - - diff -r 5981a0f7540a -r 019040fbf5f5 en/tour-merge-merge.svg --- a/en/tour-merge-merge.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,380 +0,0 @@ - - - - - - - - - - - - - - - - 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 5981a0f7540a -r 019040fbf5f5 en/tour-merge-pull.svg --- a/en/tour-merge-pull.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - 0: 0a04 - - 1: 82e5 - - 2: 057d - - 3: ff5d - - 4: b57f - - - - - - 5: ae13 - - - 6: d2b5 - - tip (and head) - head - - diff -r 5981a0f7540a -r 019040fbf5f5 en/tour-merge-sep-repos.svg --- a/en/tour-merge-sep-repos.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,466 +0,0 @@ - - - - - - - - - - - - - - - - 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 5981a0f7540a -r 019040fbf5f5 en/tour-merge.tex --- a/en/tour-merge.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,283 +0,0 @@ -\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 5981a0f7540a -r 019040fbf5f5 en/undo-manual-merge.dot --- a/en/undo-manual-merge.dot Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -digraph undo_manual { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; - "third change" -> "manual\nmerge"; - backout -> "manual\nmerge"; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/undo-manual.dot --- a/en/undo-manual.dot Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -digraph undo_manual { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/undo-non-tip.dot --- a/en/undo-non-tip.dot Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -digraph undo_non_tip { - "first change" -> "second change"; - "second change" -> "third change"; - backout [label="back out\nsecond change", shape=box]; - "second change" -> backout; - merge [label="automated\nmerge", shape=box]; - "third change" -> merge; - backout -> merge; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/undo-simple.dot --- a/en/undo-simple.dot Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -digraph undo_simple { - "first change" -> "second change"; - "second change" -> "back out\nsecond change"; -} diff -r 5981a0f7540a -r 019040fbf5f5 en/undo.tex --- a/en/undo.tex Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,763 +0,0 @@ -\chapter{Finding and fixing your mistakes} -\label{chap:undo} - -To err might be human, but to really handle the consequences well -takes a top-notch revision control system. In this chapter, we'll -discuss some of the techniques you can use when you find that a -problem has crept into your project. Mercurial has some highly -capable features that will help you to isolate the sources of -problems, and to handle them appropriately. - -\section{Erasing local history} - -\subsection{The accidental commit} - -I have the occasional but persistent problem of typing rather more -quickly than I can think, which sometimes results in me committing a -changeset that is either incomplete or plain wrong. In my case, the -usual kind of incomplete changeset is one in which I've created a new -source file, but forgotten to \hgcmd{add} it. A ``plain wrong'' -changeset is not as common, but no less annoying. - -\subsection{Rolling back a transaction} -\label{sec:undo:rollback} - -In section~\ref{sec:concepts:txn}, I mentioned that Mercurial treats -each modification of a repository as a \emph{transaction}. Every time -you commit a changeset or pull changes from another repository, -Mercurial remembers what you did. You can undo, or \emph{roll back}, -exactly one of these actions using the \hgcmd{rollback} command. (See -section~\ref{sec:undo:rollback-after-push} for an important caveat -about the use of this command.) - -Here's a mistake that I often find myself making: committing a change -in which I've created a new file, but forgotten to \hgcmd{add} it. -\interaction{rollback.commit} -Looking at the output of \hgcmd{status} after the commit immediately -confirms the error. -\interaction{rollback.status} -The commit captured the changes to the file \filename{a}, but not the -new file \filename{b}. If I were to push this changeset to a -repository that I shared with a colleague, the chances are high that -something in \filename{a} would refer to \filename{b}, which would not -be present in their repository when they pulled my changes. I would -thus become the object of some indignation. - -However, luck is with me---I've caught my error before I pushed the -changeset. I use the \hgcmd{rollback} command, and Mercurial makes -that last changeset vanish. -\interaction{rollback.rollback} -Notice that the changeset is no longer present in the repository's -history, and the working directory once again thinks that the file -\filename{a} is modified. The commit and rollback have left the -working directory exactly as it was prior to the commit; the changeset -has been completely erased. I can now safely \hgcmd{add} the file -\filename{b}, and rerun my commit. -\interaction{rollback.add} - -\subsection{The erroneous pull} - -It's common practice with Mercurial to maintain separate development -branches of a project in different repositories. Your development -team might have one shared repository for your project's ``0.9'' -release, and another, containing different changes, for the ``1.0'' -release. - -Given this, you can imagine that the consequences could be messy if -you had a local ``0.9'' repository, and accidentally pulled changes -from the shared ``1.0'' repository into it. At worst, you could be -paying insufficient attention, and push those changes into the shared -``0.9'' tree, confusing your entire team (but don't worry, we'll -return to this horror scenario later). However, it's more likely that -you'll notice immediately, because Mercurial will display the URL it's -pulling from, or you will see it pull a suspiciously large number of -changes into the repository. - -The \hgcmd{rollback} command will work nicely to expunge all of the -changesets that you just pulled. Mercurial groups all changes from -one \hgcmd{pull} into a single transaction, so one \hgcmd{rollback} is -all you need to undo this mistake. - -\subsection{Rolling back is useless once you've pushed} -\label{sec:undo:rollback-after-push} - -The value of the \hgcmd{rollback} command drops to zero once you've -pushed your changes to another repository. Rolling back a change -makes it disappear entirely, but \emph{only} in the repository in -which you perform the \hgcmd{rollback}. Because a rollback eliminates -history, there's no way for the disappearance of a change to propagate -between repositories. - -If you've pushed a change to another repository---particularly if it's -a shared repository---it has essentially ``escaped into the wild,'' -and you'll have to recover from your mistake in a different way. What -will happen if you push a changeset somewhere, then roll it back, then -pull from the repository you pushed to, is that the changeset will -reappear in your repository. - -(If you absolutely know for sure that the change you want to roll back -is the most recent change in the repository that you pushed to, -\emph{and} you know that nobody else could have pulled it from that -repository, you can roll back the changeset there, too, but you really -should really not rely on this working reliably. If you do this, -sooner or later a change really will make it into a repository that -you don't directly control (or have forgotten about), and come back to -bite you.) - -\subsection{You can only roll back once} - -Mercurial stores exactly one transaction in its transaction log; that -transaction is the most recent one that occurred in the repository. -This means that you can only roll back one transaction. If you expect -to be able to roll back one transaction, then its predecessor, this is -not the behaviour you will get. -\interaction{rollback.twice} -Once you've rolled back one transaction in a repository, you can't -roll back again in that repository until you perform another commit or -pull. - -\section{Reverting the mistaken change} - -If you make a modification to a file, and decide that you really -didn't want to change the file at all, and you haven't yet committed -your changes, the \hgcmd{revert} command is the one you'll need. It -looks at the changeset that's the parent of the working directory, and -restores the contents of the file to their state as of that changeset. -(That's a long-winded way of saying that, in the normal case, it -undoes your modifications.) - -Let's illustrate how the \hgcmd{revert} command works with yet another -small example. We'll begin by modifying a file that Mercurial is -already tracking. -\interaction{daily.revert.modify} -If we don't want that change, we can simply \hgcmd{revert} the file. -\interaction{daily.revert.unmodify} -The \hgcmd{revert} command provides us with an extra degree of safety -by saving our modified file with a \filename{.orig} extension. -\interaction{daily.revert.status} - -Here is a summary of the cases that the \hgcmd{revert} command can -deal with. We will describe each of these in more detail in the -section that follows. -\begin{itemize} -\item If you modify a file, it will restore the file to its unmodified - state. -\item If you \hgcmd{add} a file, it will undo the ``added'' state of - the file, but leave the file itself untouched. -\item If you delete a file without telling Mercurial, it will restore - the file to its unmodified contents. -\item If you use the \hgcmd{remove} command to remove a file, it will - undo the ``removed'' state of the file, and restore the file to its - unmodified contents. -\end{itemize} - -\subsection{File management errors} -\label{sec:undo:mgmt} - -The \hgcmd{revert} command is useful for more than just modified -files. It lets you reverse the results of all of Mercurial's file -management commands---\hgcmd{add}, \hgcmd{remove}, and so on. - -If you \hgcmd{add} a file, then decide that in fact you don't want -Mercurial to track it, use \hgcmd{revert} to undo the add. Don't -worry; Mercurial will not modify the file in any way. It will just -``unmark'' the file. -\interaction{daily.revert.add} - -Similarly, if you ask Mercurial to \hgcmd{remove} a file, you can use -\hgcmd{revert} to restore it to the contents it had as of the parent -of the working directory. -\interaction{daily.revert.remove} -This works just as well for a file that you deleted by hand, without -telling Mercurial (recall that in Mercurial terminology, this kind of -file is called ``missing''). -\interaction{daily.revert.missing} - -If you revert a \hgcmd{copy}, the copied-to file remains in your -working directory afterwards, untracked. Since a copy doesn't affect -the copied-from file in any way, Mercurial doesn't do anything with -the copied-from file. -\interaction{daily.revert.copy} - -\subsubsection{A slightly special case: reverting a rename} - -If you \hgcmd{rename} a file, there is one small detail that -you should remember. When you \hgcmd{revert} a rename, it's not -enough to provide the name of the renamed-to file, as you can see -here. -\interaction{daily.revert.rename} -As you can see from the output of \hgcmd{status}, the renamed-to file -is no longer identified as added, but the renamed-\emph{from} file is -still removed! This is counter-intuitive (at least to me), but at -least it's easy to deal with. -\interaction{daily.revert.rename-orig} -So remember, to revert a \hgcmd{rename}, you must provide \emph{both} -the source and destination names. - -(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 5981a0f7540a -r 019040fbf5f5 en/wdir-after-commit.svg --- a/en/wdir-after-commit.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,394 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - dfbbb33f3fa3 - - - e7639888bb2f - - 7b064d8bac5e - - - - 000000000000 - - History in repository - - - - dfbbb33f3fa3 - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - Newchangeset - - diff -r 5981a0f7540a -r 019040fbf5f5 en/wdir-branch.svg --- a/en/wdir-branch.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,418 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - e7639888bb2f - - - - 7b064d8bac5e - - - - 000000000000 - - - - - ffb20e1701ea - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - - - ffb20e1701ea - - - Pre-existing head - Newly created head (and tip) - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/wdir-merge.svg --- a/en/wdir-merge.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,425 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - 7b064d8bac5e - - - - 000000000000 - - - - - ffb20e1701ea - - - - e7639888bb2f - - First parent (unchanged) - Second parent - Parents of working directory - - - - - ffb20e1701ea - - - Pre-existing head - Newly created head (and tip) - - - - - - e7639888bb2f - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/wdir-pre-branch.svg --- a/en/wdir-pre-branch.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,364 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - e7639888bb2f - - - 7b064d8bac5e - - - - 000000000000 - - History in repository - - - - 7b064d8bac5e - - - - 000000000000 - - First parent - Second parent - Parents of working directory - - - - diff -r 5981a0f7540a -r 019040fbf5f5 en/wdir.svg --- a/en/wdir.svg Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - e7639888bb2f - - 7b064d8bac5e - - - 000000000000 - - - History in repository - - - - - e7639888bb2f - - - - 000000000000 - - First parent - Second parent - - Parents of working directory - - - - diff -r 5981a0f7540a -r 019040fbf5f5 es/00book.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/00book.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,81 @@ +% 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[spanish]{babel} +\usepackage{enumerate} +\usepackage{fullpage} +\usepackage{makeidx} +\usepackage{ifpdf} +\usepackage{graphicx} +\usepackage{pslatex} +\usepackage{fancyvrb} +\usepackage[utf8]{inputenc} %accents in spanish +% leave hyperref until last +\ifpdf +\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} +\fi + +\include{99defs} + +\title{Control Distribuido de Revisiones con Mercurial} \author{Bryan + O'Sullivan} +\date{Copyright \copyright\ 2006, 2007 Bryan O'Sullivan.\\ + Este material puede distribuirse únicamente bajo los términos y + condiciones establecidos en la versión 1.0 de la Licencia de Publicación + Abierta (OPL). Refiérase por favor al apéndice~\ref{cha:opl} para encontrar el + texto de la licencia.\\ + Este libro fue preparado a partir de + \href{http://mercurial.intuxication.org/hg/mercurial_book_es}{rev~\input{build_id}} + usando Mercurial \href{http://www.selenic.com/hg/}{rev~\input{hg_id}}.} + +\makeindex + +\begin{document} +\spanishdeactivate{<>"~} +\maketitle + +\addcontentsline{toc}{chapter}{Índice general} +\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}{Bibliografía} +\bibliographystyle{alpha} +\bibliography{99book} + +\addcontentsline{toc}{chapter}{Índice alfabético} +\printindex + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/99book.bib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99book.bib Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/99book.bib \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/99defs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99defs.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,146 @@ +% Bug ID. +\newcommand{\bug}[1]{\index{Base de datos de fallos de Mercurial + !\href{http://www.selenic.com/mercurial/bts/issue#1}{fallo + ~#1}}\href{http://www.selenic.com/mercurial/bts/issue#1}{Fallo de + Mercurial 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}, fichero}\texttt{#1}} + +% Directory name, with index entry. +\newcommand{\sdirname}[1]{\index{\texttt{#1}, directorio}\texttt{#1}} + +% Mercurial extension. +\newcommand{\hgext}[1]{\index{\texttt{#1}, extensi\'on}\texttt{#1}} + +% Command provided by a Mercurial extension. +\newcommand{\hgxcmd}[2]{\index{\texttt{#2}, comando (extensi\'on +\texttt{#1})}\index{\texttt{#1}, extensi\'on!comando \texttt{#2}}``\texttt{hg #2}''} + +% Mercurial command. +\newcommand{\hgcmd}[1]{\index{\texttt{#1}, comando}``\texttt{hg #1}''} + +% Mercurial command, with arguments. +\newcommand{\hgcmdargs}[2]{\index{\texttt{#1}, comando}``\texttt{hg #1 #2}''} + +\newcommand{\tplkword}[1]{\index{\texttt{#1}, palabra clave de +plantilla}\index{palabras clave de plantilla!\texttt{#1}}\texttt{#1}} + +\newcommand{\tplkwfilt}[2]{\index{\texttt{#1}, palabra clave de plantilla!filtro +\texttt{#2}}\index{filtros de plantilla!\texttt{#2}}\index{\texttt{#2}, filtro +de plantilla}\texttt{#2}} + +\newcommand{\tplfilter}[1]{\index{filtros de +plantilla!\texttt{#1}}\index{\texttt{#1}, filtro de plantilla}\texttt{#1}} + +% Shell/system command. +\newcommand{\command}[1]{\index{\texttt{#1}, comando de sistema}\texttt{#1}} + +% Shell/system command, with arguments. +\newcommand{\cmdargs}[2]{\index{\texttt{#1} comando de sistema}``\texttt{#1 #2}''} + +% Mercurial command option. +\newcommand{\hgopt}[2]{\index{\texttt{#1}, comando!opci$BC3(Bn \texttt{#2}}\texttt{#2}} + +% Mercurial command option, provided by an extension command. +\newcommand{\hgxopt}[3]{\index{\texttt{#2}, comando (extensi$BC3(Bn +\texttt{#1})!opci$BC3(Bn \texttt{#3}}\index{\texttt{#1}, extensi$BC3(Bn!comando +\texttt{#2}!opci$BC3(Bn\texttt{#3}}\texttt{#3}} + +% Mercurial global option. +\newcommand{\hggopt}[1]{\index{opciones globales!opci$BC3(Bn \texttt{#1}}\texttt{#1}} + +% Shell/system command option. +\newcommand{\cmdopt}[2]{\index{\texttt{#1}, comando!opci$BC3(Bn \texttt{#2}}\texttt{#2}} + +% Command option. +\newcommand{\option}[1]{\texttt{#1}} + +% Software package. +\newcommand{\package}[1]{\index{\texttt{#1}, paquete}\texttt{#1}} + +% Section name from a hgrc file. +\newcommand{\rcsection}[1]{\index{\texttt{hgrc}, fichero!secci$BC3(Bn \texttt{#1}}\texttt{[#1]}} + +% Named item in a hgrc file section. +\newcommand{\rcitem}[2]{\index{\texttt{hgrc}, fichero!secci$BC3(Bn +\texttt{#1}!entrada \texttt{#2}}\texttt{#2}} + +% hgrc file. +\newcommand{\hgrc}{\index{fichero de configuraci$BC3(Bn!\texttt{hgrc} + (Linux/Unix)}\index{\texttt{hgrc}, fichero de configuraci$BC3(Bn}\texttt{hgrc}} + +% Mercurial.ini file. +\newcommand{\hgini}{\index{fichero de configuraci$BC3(Bn!\texttt{Mercurial.ini} + (Windows)}\index{\texttt{Mercurial.ini}, fichero de configuraci$BC3(Bn}\texttt{Mercurial.ini}} + +% Hook name. +\newcommand{\hook}[1]{\index{\texttt{#1}, gancho}\index{ganchos!\texttt{#1}}\texttt{#1}} + +% Environment variable. +\newcommand{\envar}[1]{\index{\texttt{#1}, variable de entorno}\index{variables +de entorno!\texttt{#1}}\texttt{#1}} + +% Python module. +\newcommand{\pymod}[1]{\index{\texttt{#1}, m$BC3(Bdulo}\texttt{#1}} + +% Python class in a module. +\newcommand{\pymodclass}[2]{\index{\texttt{#1}, m$BC3(Bdulo!clase \texttt{#2}}\texttt{#1.#2}} + +% Python function in a module. +\newcommand{\pymodfunc}[2]{\index{\texttt{#1}, m$BC3(Bdulo!funci$BC3(Bn \texttt{#2}}\texttt{#1.#2}} + +% Note: blah blah. +\newsavebox{\notebox} +\newenvironment{note}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Nota:}\space}% + {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}} +\newenvironment{caution}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Precauci$BC3(Bn:}\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}[2][]{\includegraphics[#1]{#2}} +\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}, comando}} + +% Reference entry for a command option with long and short forms. +\newcommand{\optref}[3]{\subsubsection{\hgopt{#1}{--#3}, tambi$BC)(Bn \hgopt{#1}{-#2}}} + +% Reference entry for a command option with only long form. +\newcommand{\loptref}[2]{\subsubsection{opci$BC3(Bn \hgopt{#1}{--#2}}} + +% command to generate a footnote to be used as a translator's note +\newcommand{\ndt}[1]{\footnote{\textbf{N. del T.} #1}} + + +%%% Local Variables: +%%% mode: yatex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/Leame.1st --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Leame.1st Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,508 @@ += Parámetros de Organización = + + * Se mantienen los nombres de los archivos + * Se traduce solamente el contenido + * Copie los archivos de en a es y tradúzcalos + * Las gráficas son tan importantes como los archivos + de texto, ya han sido traducidas + * Encoding UTF-8 para las tildes, eñes y demás + * Ancho de línea de 70 caracteres + += ¿Cómo contribuir? = +Obtenga la copia : +hg clone http://mercurial.intuxication.org/hg/mercurial_book_es/ + +Esto le ofrecerá un clon del repositorio en el directorio recién +creado '''mercurial_book_es''': + +mercurial_book_es +| +|-- en +|-- es +|-- examples +|-- html +`-- sillybench + +El directorio de trabajo es '''es'''. + + +Una vez que haya traducido o aplicado correcciones a los archivos de +su copia local, haga un commit + + hg commit -m "comentario descriptivo de lo que hizo" + +Siempre mantenga actualizado su repositorio local + hg pull + hg update + +Hay dos formas de hacer la contribución, primero envíe un correo a +igor@tamarapatino.org indicando lo que desea hacer, se le puede +otorgar permiso de escritura en el repositorio, o si lo prefiere, +puede enviar un parche (patch). Describimos a continuación los dos +procedimientos : Repositorio Público y Parches. Es preferible el +repositorio público frente a los parches, puesto que estos segundos +pueden tardar en propagarse más. + +== Repositorio Público == +Este sería el método preferido para que los cambios que usted haga +automáticamente queden en el repositorio y todos los traductores +podamos contar con la información rápidamente. + +Una vez que usted haya recibido la información necesaria, habiendo +elegido su usuario y su clave podrá "publicar" (push). + +Como este es un sistema distribuido, después de hacer la +consignación (commit), deberá publicarlo. + + hg push + +Se le solicitará su usuario y clave. + +== Parches == +Este método exige que alguien reciba el parche y haga manualmente la +aplicación del mismo, ese alguien es igor@tamarapatino.org por ahora, +después de haber hecho commit en su repositorio local, revise su log. + + hg log | head + +Esta última orden le permitirá establecer la última revisión que se +consignó en su repositorio local, su identificador de revisión tendrá +el formato número:hash. Generaría el archivo +/tmp/patchparahgbook.patch con la orden + + hg -o /tmp/patchparahgbook.patch REV + +donde REV es el identificador de revisión que debió haber encontrado. + += Traducción/Revisión = + +En esta sección indicamos quienes están traduciendo +y quienes revisando lo traducido. Coloque su nombre +para que los demás colaboradores sepan en dónde +enfocar sus esfuerzos. + +Indique qué archivos está traduciendo y/o revisando en +la lista siguiente. Cada archivo debe ser traducido y +revisado antes de poder considerarlo terminado. El revisor +no puede ser la misma persona que hizo la traducción. + +Cada traductor puede traducir o revisar el archivo que +desee, teniendo siempre en cuenta los archivos que ya tengan +alguien a cargo y escogiendo en la medida de lo posible +otros que no lo tengan. Los arreglos de 'typos' y problemas +de ortografía son siempre bienvenidos. + +== Archivos en proceso de traducción == +||'''archivo''' ||'''traductor'''||'''Estado'''||'''Inicio'''|| '''Fin''' || +|| 00book.tex || Igor Támara || 100% || 16/10/2008 || 16/10/2008 || +|| preface.tex || Javier Rojas || 100% || 18/10/2008 || 19/10/2008 || +|| intro.tex || Igor Támara || 100% || 08/11/2008 || 09/11/2008 || +|| tour-basic.tex || Javier Rojas || 100% || 19/10/2008 || 27/10/2008 || +|| tour-merge.tex || Javier Rojas || 100% || 28/10/2008 || 03/11/2008 || +|| concepts.tex || Javier Rojas || 100% || 03/11/2008 || 23/11/2008 || +|| daily.tex || Igor Támara || 100% || 19/10/2008 || 26/10/2008 || +|| collab.tex || Igor Támara || 100% || 10/11/2008 || 06/12/2008 || +|| filenames.tex || Javier Rojas || 100% || 27/11/2008 || 12/01/2008 || +|| branch.tex || Igor Támara || 100% || 16/10/2008 || 19/10/2008 || +|| undo.tex || Igor Támara || 100% || 26/10/2008 || 07/11/2008 || +|| hook.tex || Javier Rojas || 100% || 01/12/2008 || 04/01/2009 || +|| template.tex || Igor Támara || 100% || 27/12/2008 || 01/01/2009 || +|| mq.tex || Igor Támara || 100% || 06/12/2008 || 13/12/2008 || +|| mq-collab.tex || Javier Rojas || 100% || 04/01/2009 || 08/01/2009 || +|| hgext.tex || Igor Támara || 100% || 13/12/2008 || 16/12/2008 || +|| cmdref.tex || Igor Támara || 100% || 01/01/2009 || 01/01/2009 || +|| mq-ref.tex || Igor Támara || 100% || 06/01/2009 || 10/01/2009 || +|| srcinstall.tex || Igor Támara || 100% || 01/01/2009 || 01/01/2009 || +|| license.tex || Igor Támara || 100% || 16/12/2008 || 16/12/2008 || + +== Archivos en proceso de revisión == +||'''archivo''' || '''revisor''' ||'''Estado'''||'''Inicio'''|| '''Fin''' || +|| 00book.tex || Javier Rojas || 100% || 18/01/2009 || 18/01/2009 || +|| branch.tex || Javier Rojas || 100% || 25/01/2009 || 25/01/2009 || +|| preface.tex || || || || || +|| daily.tex || Javier Rojas || 100% || 25/01/2009 || 29/01/2009 || +|| tour-basic.tex || || || || || +|| undo.tex || Javier Rojas || || || || +|| tour-merge.tex || || || || || +|| concepts.tex || || || || || +|| intro.tex || Javier Rojas || 100% || 12/01/2009 || 12/01/2009 || +|| collab.tex || Javier Rojas || 0% || 29/01/2009 || || +|| mq.tex || || || || || +|| hgext.tex || || || || || +|| template.tex || || || || || +|| mq-collab.tex || || || || || +|| mq-ref.tex || || || || || +|| cmdref.tex || || || || || +|| license.tex || || || || || +|| srcinstall.tex || || || || || + +== Archivos terminados == +||'''archivo''' ||'''Inicio'''|| '''Fin''' || +|| 00book.tex || 16/10/2008 || 18/01/2009 || +|| intro.tex || 08/11/2008 || 12/01/2009 || +|| branch.tex || 16/10/2008 || 25/01/2009 || +|| daily.tex || 19/10/2008 || 29/01/2009 || + += Unificación de Términos de Traducción = +Por favor mantenga esta lista en orden alfabético + +La mayor parte del texto a continuación fue tomado del glosario de la +traducción del libro de subversion al idioma español. + +Pequeño glosario de términos traducidos. Aquí deben ponerse esos +"bonitos palabros" que tanto nos ha costado traducir para evitar +inconsistencias en la traducción por varias personas. Normalmente +son técnicos, pero puede incluirse cualquier "giro" o expresión que +consideremos útil mantener y repetir a lo largo de la traducción. +En el libro final posiblemente se añada una versión de este fichero +como apéndice. + +Para incluir algo, hay que especificar la expresión en inglés, su +versión traducida, y una pequeña explicación como justificación. + + Alice: Alicia + Anne: Ana + Back out: Retroceder + Binary test: Prueba binaria + Bob : Roberto + Branch: Rama + Bug: Fallo + Build Script: Guión de construcción + Builtin: integrada/o + Bundle: Agrupamiento + Bundled: Incluído o agrupado + Changelog: Bitácora de Cambios + Changeset: Conjunto de Cambios + Command: Orden + Commit: Consignar + Core: alma + Directory: Directorio + Escape Sequence: Secuencia de control + File: fichero + Filelog: fichero de registro + Fold: Integrar + Fork: Bifurcación + Hash: No se traduce + Head: Principal. En el contexto de revisiones HEAD se sugiere usar "frente" + Hook: Gancho + Merge: Fusión + Milestone: Etapa + Mistake: Equivocación, cometida por un humano + Output: salida o despliegue + Patch: Parche + Path: Ruta de archivo + Pointer: apuntador + Pop: Sustraer, la contraparte push, será publicar + Probe: Sondeo + Pull: Jalar + Push: Publicar. En el contexto de parches introducir. + Queue: Cola + Release: Versión o liberación de versión + Revlog: Bitácora de revisiones + Roll back: NO se traduce Ver más abajo + Snapshot: instantánea + Snippet: Recorte de código + Stack: pila + Stripped: + Sprint: sprint + Tarball: paquete de cambios + Timestamp : marca de tiempo + Tip: punta + Update: actualización + Upstream: principal, mantenedor principal. De acuerdo al contexto. + +abort -> cancelar + +ancestry -> ascendencia + La traducción literal concuerda con el significado que se le + da al mismo término en la jerga de Subversion. + +API, GUI -> no se traduce + La primera vez que aparecen, poner nota del traductor indicando + qué significan las siglas y su traducción al castellano. De + hecho, verificar la aparición de las notas de traductor en el + sitio correcto debería ser una entrada del fichero TODO... + +back-end -> ??? + Se refiere al término opuesto de front-end. En el octavo + capítulo se usa al menos tres veces para referirse al "motor" + que hay detrás de la capa de base de datos. Hmmm... pero motor + suena raro... + +backup -> copia de seguridad + Obtenido del glosario ORCA. + +Blanket Access Control -> Control de acceso simple + Por ahora no se me ocurre mejor traducción. Aquí blanket se + refiere a un control muy genérico, con poca granularidad. + +branching and merging -> crear ramas y fusionarlas + Aunque branch está bien traducido como rama o rama de desarrollo + (en su versión "verbose"), no hay forma de hacer de un sustantivo + un verbo. Por lo tanto, se crean, borran y fusionan ramas. + +browse -> navegar + Usado con frecuencia para navegar por directorios o repositorios. + +build -> comodín + No es que se traduzca como comodín, sino que en función del + contexto es una de esas palabras que significan muchas cosas y + no es posible traducirla literalmente. Por ejemplo, cuando se + usa como verbo se suele referir a compilar código fuente. En + cambio, cuando se usa como sustantivo se suele referir a una + versión particular del fichero binario ejecutable de un software, + resultado de una compilación previa. Cuidado con esto. Anotar + aquí traducciones realizadas para comparar. + + ...using and building Subversion... + ...usando y compilando Subversion... + +changeset -> ??? + Aparentemente conjunto de cambios. No puede ser traducido + como parche, el libro inglés indica que un "changeset" es un + parche con nombre único. Por ahora dejar en "changeset", ya se + buscará algo en el futuro. Para ver la descripción del libro, + buscar en ch04.xml la frase "Subversion y los changesets". + +cheap copy -> copia ligera + Otras posibilidades barajadas son copia barata o liviana. + Veremos si en el futuro éstas suenan mejor. + +click -> haga clic, pulse + Traducción obtenida del glosario. Parece ser que click sólo se + deja tal cual cuando se refiere a un sonido mecánico. Cuando + se refiere a pulsar el botón del ratón se traduce como clic. + +CVS -> No se traduce + +DAV share -> recurso DAV compartido + No tengo ni idea de lo que es un DAV share, así que cuando me + entere (o alguien lo haga), que cambie esta entrada del glosario. + +directory -> directorio + Entre carpeta y directorio se prefiere directorio. + +email -> correo electrónico + Entre las posibilidades de dejar la palabra tal cual, añadir un + guión (e-mail) y poner la traducción, se prefiere la traducción + completa. + +FAQ -> FAQ + Se deja tal cual pues se explica en el primer párrafo del prólogo + como una nota del traductor, y porque es muy frecuente ver su + uso en español. PyRF, PUFs o PFs son realmente desconcertantes. + +file -> fichero + Entre archivo y fichero se prefiere fichero. + +file path -> ruta del fichero + Cuando se ve path a secas, la forma más común de traducirlo + es decir ruta a secas igualmente. Bueno, el glosario de ORCA + también da como válidos camino y trayectoria. A ser posible + usar ruta si el contexto de la frase así lo favorece, en caso + contrario probar con camino o trayectoria. + +hash table -> tabla hash + Sugerido por Miguel Pérez Ibars. También encontrado en el + glosario ORCA. + +history -> Depende del contexto. Cuando se use en el contexto del repositorio + (the history of the repository), debe usarse el término historial. En otro + caso, historia. Valga anotar que ambas traducciones son aceptadas (al menos + en wordreference.com). + +hook, to hook -> gancho, enganchar + Usado en terminología de programación para indicar que el usuario + tiene un mecanismo estándar para modificar el comportamiento de + un dispositivo existente. Un ejemplo a nivel de programación + son las funciones "callback" que se pasan como parámetros, + o a nivel de sistema, scripts que existen en un directorio + concreto y que se ejecutan por el servidor de Subversion para + realizar tareas personalizadas por el administrador. + +ignore pattern -> ignorar patrones, pero patrones de exclusión + Subversion permite que ciertas opciones almacenen patrones + con los que se ignoran ficheros no versionados. Cuando la + frase original usa el verbo, se puede traducir como ignorar + directamente, pero cuando se usa a modo de sustantivo, + es mejor traducirlo como "patrón de exclusión" en lugar de + "patrón a ignorar". + +language -> lenguaje o idioma + Precisamente para diferenciar si nos estamos refiriendo a un + lenguaje de programación o a un lenguaje hablado por humanos, + se usará idioma en este último caso. + +language binding, SWIG binding, wrapper -> interfaz, enlace, ligadura, envoltorio... + Dependiendo del contexto, y desde un punto de vista personal, + se puede traducir esta palabra de muchas maneras. Una manera + genérica es decir "Interfaz con el lenguaje X", o "Ligadura con + el lenguaje X". Habitualmente, cuando se habla de una interfaz, + se está hablando de un binding ligero, o que únicamente permite + a hacer llamadas a funciones del lenguaje de programación A + desde el lenguaje de programación B. + + En cambio, se suele hablar de wrapper cuando el código entre + el lenguaje A y B es algo más complejo, o adapta el estilo de + programación del lenguaje A al del lenguaje B. Un ejemplo de esto + último sería una librería en C cuyo binding en Perl/Ruby/Python + fuese orientado a objetos. + + Aparte, wrapper también se usa cuando se habla de una función + o librería que engloba o asimila otra menor, con el propósito + de extender su funcionalidad o hacerla más fácil de usar + de cara al usuario, sin necesidad de cruzar ninguna barrera + "intra-lenguaje". + + Por lo tanto, hay que decidir con cuidado el contexto de la + palabra binding o wrapper, y escoger una. En el capítulo octavo + hay varios usos, aunque como son relativos a SWIG, se habla de + interfaces o envoltorios simplificados, puesto que se generan + de manera automática y no hay ninguna "conversión" en el estilo + de uso. + +lazy copy -> copia vaga + Horrible traducción literal. ¿Sugerencias? + copia perezosa + +location (repository) -> ubicación (del repositorio) + Cuidado, no traducir como localización, que es la acción y + efecto de localizar. + +log, log message -> informe de cambios, mensaje del informe de cambios + Traducción extraída del manual de CVS en español. La traducción + de "log message" es muy larga, pero únicamente porque no se + tiene contexto alguno. Con contexto suele ser posible omitir + la palabra informe si se considera apropiado. + +memory pool -> área de memoria + Traducción temporal hasta que se revise algún libro en castellano + de programación que use el mismo término. + +merge -> fusionar cambios, fusión + Obtenida referencia del manual de CVS en castellano, la otra + alternativa obvia para traducir merge es mezclar. No obstante, + mezclar tiene una connotación de azar al realizar la mezcla. Se + mezclan líquidos, ingredientes, etc. En cambio, un "merge" es + cualquier cosa menos un proceso realizado al azar (especialmente + si ha habido conflictos). En caso de traducir como sustantivo, + fusión vuelve a "sonar mejor" que mezcla, que por alguna razón me + suena a un combustible especial usado en vehículos de transporte. + +namespace -> espacio de nombrado, + Tecnicismo del C++, obtenido de la traducción del libro + "Pensar en C++", en concreto la sección 3.2 disponible en + http://arco.inf-cr.uclm.es/~dvilla/pensar_en_C++/ch02s03.html#id2589039. + +open source -> código fuente abierto + Referencia: http://es.tldp.org/ORCA/glosario.html#O + +plugins -> módulos + El término fue extraído del glosario de ORCA. + +repository -> repositorio + No hay mucha alternativa, ¿verdad? + +roll back -> No se traduce + El significado igual que en los ambientes + de sistemas manejadores de bases de datos se refiere a la atomicidad + e integridad al devolver un conjunto de acciones que permitan dejar + el repositorio en un estado consistente previo. + +repository layou t-> estructura del repositorio En referencia a cómo + están organizados los directorios. + +schedule -> programa o planifica + Parece más correcta la opción programa, en el sentido de pensar en + hacer algo. + schedule foo to be added -> programa la adición de foo + +switch -> cambiar + Únicamente cuando se habla del comando svn switch, no como la + palabra switch aislada, que significa parámetro o interruptor. + En el contexto de svn switch, traducirlo como cambiar. Quizás + traducir como reubicar una vez haya suficiente material traducido + y una lectura final sugiera una u otra traducción. + +tag, tagging -> etiqueta, etiquetar + Expresión ya común en español. + +three-way differencing program -> programa de diferenciación a tres bandas + Una diferenciación "normal" es generar las diferencias entre + dos ficheros. Una diferenciación a tres bandas es comparar las + diferencias entre tres ficheros, y se usa cuando se intentan + fusionar los cambios de una copia local junto con los cambios + recibidos del repositorio (por ejemplo, otra persona ha cambiado + el fichero sobre el que trabajábamos). + +timestamp, datestamp -> marca de tiempo, fecha y hora + Esa traducción viene del ORCA. No obstante en muchos casos es + más claro traducirla como "fecha de fichero" o "fecha" a secas. + Ejemplo: ...will show the modification timestamp. -> mostrará + la fecha de modificación. Decir "mostrará la marca de tiempo de + la modificación" o algo así es una traducción demasiado literal, + así que ojo con el contexto. + +track -> seguir, monitorear + este término suele ser usado cuando se habla de archivos, y de llevar la + pista o monitorear los cambios en un archivo. + +trunk -> tronco + Se refiere al nombre que recibe la línea de desarrollo + principal del repositorio que no está asociada a ninguna rama + en particular. Traducción obtenida del manual de CVS en español. + +Unix-like systems -> sistemas tipo unix + +URL -> No se traduce + +working copy -> copia (de trabajo) local/activa + Traducción similar a la de "commit data" (razonamiento + cliente-servidor). + += Términos a no-usar = +Evite el uso de estos términos en la traducción aún si son de uso +común para usted; el consenso general de la comunidad hispana puede +ser diferente del que usted tenga ;) + + * "archivo". Use "fichero" en su lugar. + * "carpeta". Use "directorio". + * "la historia". Use "el historial" (por supuesto, cuando se refiera al + historial del repositorio) + += Diferencias de redacción = +Hay varias expresiones que pueden terminar siendo traducidas de formas +diferentes por los traductores, y que aún siendo ambas correctas, ponen en +evidencia que el trabajo fue llevado a cabo por varias personas y que el estilo +que ellas usan difiere. Una vez terminada la traducción habrá que buscar cuáles +son éstos términos y decidirse por uno solo de ellos. A continuación se presenta +una lista de los términos o expresiones encontrados hasta ahora + + * comando - orden. Ambos son la traducción para "command". Parece que la + traducción más adecuada es "comando" + (http://www.wordreference.com/es/translation.asp?tranword=command) + * kernel - núcleo. + * Colas de Mercurial - colas de Mercurial. Creo que lo mejor es revisar qué + usó el autor (y rogar que él no haya sido inconsistente :P) + * armar - compilar - construir. Build, compile. Más que todo "build" + * daemonio - demonio. daemon + * kernel - núcleo. + * la URL - el URL + += Notas del traductor = +Por favor use el comando \ndt para insertar notas del traductor. Este +comando requiere un argumento. Por ejemplo: \ndt{Del inglés del original.} + += Para compilar = +He aquí algunas dependencias para Debian: + +apt-get install texlive-latex-extra tex4ht diffstat patchutils \ + inkscape graphviz texlive-pdfetex + += Traductores = +Por favor mantenga esta lista en orden alfabético de acuerdo al +apellido. + + * Javier Rojas + * Igor Támara + * Su nombre diff -r 5981a0f7540a -r 019040fbf5f5 es/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Makefile Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,223 @@ +# 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 \ + ../html/index.es.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}, fechado {date|isodate},\n') + +hg-version = $(shell hg version -q | \ + sed 's,.*(versión \(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) examples $(image-pdf) + $(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) examples $(image-html) bookhtml.cfg + $(call htlatex,$@,$<) + +html/split/hgbook.html: $(sources) examples bookhtml.cfg + $(call htlatex,$@,$<,2) + +# Produce 90dpi PNGs for the web. + +%.png: %.svg fixsvg + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg + +%.svg: %.dot + dot -Tsvg -o $@ $< + +# Produce eps & pdf for the pdf + +%.pdf: %.eps + epstopdf $< + +%.eps: %.svg + ./fixsvg $< + inkscape -E $@ $<-tmp.svg + rm $<-tmp.svg + +%.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 html/onepage/hgbook.html dist/onepage.html + ln -s index.es.html dist/index.html + cp $(dist-sources) dist + +rsync: install + rsync -avz --delete dist/ ikks@sulaco.devnull.li:public_html/hgbook/ diff -r 5981a0f7540a -r 019040fbf5f5 es/bookhtml.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/bookhtml.cfg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/bookhtml.cfg \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/branch.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/branch.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,412 @@ +%% vim: tw=70 encoding=utf8 +\chapter{Administración de versiones y desarrollo ramificado} +\label{chap:branch} + +Mercurial ofrece varios mecanismos que le permiten administrar un +proyecto que avanza en múltiples frentes simultáneamente. Para +entender estos mecanismos, demos un vistazo a la estructura usual de +un proyecto de software. + +Muchos proyectos de software liberan una versión ``mayor'' que contiene +nuevas características substanciales. En paralelo, pueden liberar +versiones ``menores''. Usualmente éstas son idénticas a las +versiones mayores en las cuales están basadas, pero con arreglos para +algunos fallos. + +En este capítulo, comenzaremos hablando de cómo mantener registro de +etapas del proyecto como las liberaciones de una +versión. Continuaremos hablando del flujo de trabajo entre las +diferentes fases de un proyecto, y cómo puede ayudar Mercurial a +aislar y administrar tal trabajo. + +\section{Dar un nombre persistente a una revisión} + +Cuando usted decide otorgar a una revisión el nombre particular de una +``versión'', es buena idea grabar la identidad de tal revisión. +Esto le permitirá reproducir dicha versión en una fecha posterior, +para cualquiera que sea el +propósito que se tenga en ese momento (reproducir un fallo, portar +a una nueva plataforma, etc). +\interaction{tag.init} + +Mercurial le permite dar un nombre permanente a cualquier revisión +usando la orden \hgcmd{tag}. Sin causa de sorpresa, esos nombres se llaman +``tags'' (etiquetas). +\interaction{tag.tag} + +Una etiqueta no es más que un ``nombre simbólico'' para una revisión. Las +etiquetas existen únicamente para su conveniencia, brindándole una forma +permanente y sencilla de referirse a una revisión; Mercurial no +interpreta de ninguna manera los nombres de las etiquetas que usted use. +Mercurial tampoco impone restricción alguna al nombre de una etiqueta, más +allá de lo necesario para asegurar que una etiqueta pueda procesarse sin +ambigüedades. El nombre de una etiqueta no puede tener ninguno de los +siguientes caracteres: +\begin{itemize} +\item Dos puntos (ASCII 58, ``\texttt{:}'') +\item Retorno de carro (return) (ASCII 13, ``\Verb+\r+'') +\item Nueva línea (ASCII 10, ``\Verb+\n+'') +\end{itemize} + +Puede usar la orden \hgcmd{tags} para ver las etiquetas presentes en +su repositorio. Al desplegarse, cada revisión marcada se identifica +primero con su nombre, después con el número de revisión y finalmente con +un hash único de la revisión. +\interaction{tag.tags} +Note que \texttt{tip} aparece en en listado generado por \hgcmd{tags}. La etiqueta +\texttt{tip} es una etiqueta ``flotante'' especial, que identifica siempre +la revisión más reciente en el repositorio. + +Al desplegar la orden \hgcmd{tags}, las etiquetas se listan en orden +inverso, por número de revisión. Lo que significa usualmente que las +etiquetas más recientes se listan antes que las más antiguas. También +significa que la etiqueta \texttt{tip} siempre aparecerá como primera +etiqueta listada al desplegar la orden \hgcmd{tags}. + +Cuando usted ejecuta \hgcmd{log}, si se muestra una revisión que tenga +etiquetas asociadas a ella, se imprimirán tales etiquetas. +\interaction{tag.log} + +Siempre que requiera indicar un~ID de revisión a una orden de +Mercurial, aceptará un nombre de etiqueta en su lugar. Internamente, +Mercurial traducirá su nombre de etiqueta en el~ID de revisión +correspondiente, y lo usará. +\interaction{tag.log.v1.0} + +No hay límites en la cantidad de etiquetas por repositorio, o la cantidad +de etiquetas que una misma revisión pueda tener. Siendo prácticos, no es +muy buena idea tener ``demasiadas'' (la cantidad variará de un +proyecto a otro), debido a que la intención es ayudarle a encontrar +revisiones. Si tiene demasiadas etiquetas, la facilidad de usarlas +para identificar revisiones disminuirá rápidamente. + +Por ejemplo, si su proyecto tiene etapas (milestones) frecuentes, de pocos +días, es perfectamente razonable asignarle una etiqueta a cada una de +ellas. Pero si tiene un sistema de construcción automática de binarios +que asegura que cada revisión puede generarse limpiamente, estaría +introduciendo mucho ruido si se usara una etiqueta para cada generación +exitosa. Más bien, podría usar tags para generaciones fallidas +(\textexclamdown en +caso de que estas sean raras!), o simplemente evitar las etiquetas para +llevar cuenta de la posibilidad de generación de binarios. + + +Si quiere eliminar una etiqueta que no desea, use +\hgcmdargs{tag}{--remove}. +\interaction{tag.remove} +También puede modificar una etiqueta en cualquier momento, para que +identifique una revisión distinta, simplemente usando una nueva orden +\hgcmd{tag}. Deberá usar la opción \hgopt{tag}{-f} para indicarle a +Mercurial que \emph{realmente} desea actualizar la etiqueta. +\interaction{tag.replace} +De todas maneras habrá un registro permanente de la antigua identidad +de la etiqueta, pero Mercurial no la usará. Por lo tanto no hay +problema al marcar con una etiqueta una revisión incorrecta; lo único +que debe hacer es mover la etiqueta hacia la revisión correcta tan +pronto como localice el error. + +Mercurial almacena las etiquetas en un fichero controlado por revisiones en +su repositorio. Si ha creado etiquetas, las encontrará en un fichero +llamado \sfilename{.hgtags}. Cuando invoca la orden \hgcmd{tag}, +Mercurial modifica este fichero, y hace la consignación del cambio al +mismo automáticamente. Esto significa que cada vez que ejecuta +\hgcmd{tag}, verá un conjunto de cambios correspondiente en la salida +de \hgcmd{log}. +\interaction{tag.tip} + +\subsection{Manejo de conflictos entre etiquetas durante una fusión} + +Usualmente no tendrá que preocuparse por el fichero \sfilename{.hgtags}, +pero a veces hace su aparición durante una fusión. El formato del +fichero es sencillo: Consiste de una serie de líneas. Cada línea +comienza con un hash de conjunto de cambios, seguido por un espacio, +seguido por el nombre de una etiqueta. + +Si está resolviendo un conflicto en el fichero \sfilename{.hgtags} +durante una fusión, hay un detalle para tener en cuenta al modificar +el fichero \sfilename{.hgtags}: +cuando Mercurial procesa las etiquetas en el repositorio, \emph{nunca} +lee la copia de trabajo del fichero \sfilename{.hgtags}. En cambio, +lee la versión \emph{consignada más reciente} del fichero. + +Una consecuencia desafortunada de este diseño es que usted no puede +verificar que su fichero \sfilename{.hgtags} fusionado sea correcto hasta +\emph{después} de haber consignado un cambio. Así que si se +encuentra resolviendo un conflicto en \sfilename{.hgtags} durante una +fusión, asegúrese de ejecutar la orden \hgcmd{tags} después de +consignar. Si encuentra un error en el fichero \sfilename{.hgtags}, +la orden reportará el lugar del error, que podrá arreglar y después +consignar. Posteriormente ejecute de nuevo la orden \hgcmd{tags} para +asegurarse de que su arreglo fue aplicado correctamente . + +\subsection{Etiquetas y clonado} + +Puede haber notado que la orden \hgcmd{clone} tiene la opción +\hgopt{clone}{-r} que le permite clonar una copia exacta del +repositorio hasta un conjunto de cambios específico. El nuevo clon no +tendrá historial posterior a la revisión que usted haya +especificado. Esto tiene una interacción con etiquetas que puede +sorprender a los desprevenidos. + +Recuerde que una etiqueta se almacena como una revisión al fichero +\sfilename{.hgtags}, así que cuando usted crea una etiqueta, el +conjunto de cambios en el cual ésta se almacena necesariamente se +refiere a un conjunto de cambios anterior. Cuando ejecuta +\hgcmdargs{clone}{-r foo} para clonar un repositorio hasta la etiqueta +\texttt{foo}, el nuevo clon \emph{no contendrá el historial que creo +la etiqueta} que usó para clonar el repositorio. El resultado es que tendrá +exactamente el subconjunto correcto del historial del proyecto en el +nuevo repositorio, pero, \emph{no} la etiqueta que podría haber esperado. + +\subsection{Cuando las etiquetas permanentes son demasiado} + +Dado que las etiquetas de Mercurial están controladas por revisiones y se +llevan en el historial del proyecto, todas las personas involucradas +verán las etiquetas que usted haya creado. El hecho de dar nombres a las +revisiones tiene usos más allá que simplemente hacer notar que la +revisión \texttt{4237e45506ee} es realmente \texttt{v2.0.2}. Si está +tratando de encontrar un fallo sutil, posiblemente desearía colocar una +etiqueta recordándole algo como ``Ana vio los síntomas en esta revisión''. + +Para estos casos, lo que usted posiblemente desearía serían etiquetas +\emph{locales}. Puede crear una etiqueta local con la opción~\hgopt{tag}{-l} +de la orden \hgcmd{tag}. Esto guardará la etiqueta en un fichero llamado +\sfilename{.hg/localtags}. A diferencia de \sfilename{.hgtags}, +\sfilename{.hg/localtags} no está controlado por revisiones. +Cualquier etiqueta que usted cree usando \hgopt{tag}{-l} se mantendrá +local al repositorio en el que esté trabajando en ese momento. + +\section{El flujo de cambios---El gran cuadro vs. el pequeño} + +Retomando lo mencionado en el comienzo de un capítulo, pensemos en el +hecho de que un proyecto tiene muchas piezas concurrentes de trabajo +en desarrollo al mismo tiempo. + +Puede haber prisa por una nueva versión ``principal''; una nueva +versión con un arreglo de fallo a la última versión; y una versión de +``mantenimiento correctivo'' a una versión antigua que ha entrado en +modo de mantenimiento. + +Usualmente la gente se refiere a esas direcciones +concurrentes de desarrollo como ``ramas''. Sin embargo, ya hemos visto que +en varias ocasiones Mercurial trata a \emph{todo el historial} como +una serie de ramas y fusiones. Realmente lo que tenemos aquí es dos +ideas que se relacionan periféricamente, pero que en esencia comparten +un nombre. +\begin{itemize} +\item ``El gran cuadro'' Las ramas representan un barrido de la + evolución del proyecto; la gente les da nombres y hablan acerca de + ellas en sus conversaciones. +\item ``El cuadro pequeño'' Las ramas son artefactos de las + actividades diarias de desarrollar y fusionar cambios. Exponen la + narrativa de cómo se desarrolló el código. +\end{itemize} + +\section{Administrar ramas en repositorios estilo gran cuadro} + +En Mercurial la forma más sencilla de aislar una rama del ``gran +cuadro'' es a través de un repositorio dedicado. Si cuenta con un +repositorio compartido existente ---llamémoslo +\texttt{myproject}---que alcanzó la etapa ``1.0'', puede comenzar a +prepararse para versiones de mantenimiento futuras a partir de la +versión~1.0 marcando con una etiqueta la revisión con la cual preparó la versión~1.0. +\interaction{branch-repo.tag} +Ahora puede clonar un repositorio compartido nuevo +\texttt{myproject-1.0.1} con tal etiqueta. +\interaction{branch-repo.clone} + +Posteriormente, si alguien necesita trabajar en la reparación de un +fallo debería dirigirse a la liberación de versión~1.0.1 que viene en +camino, ellos clonarían el repositorio \texttt{myproject-1.0.1}, +harían sus cambios y los empujarían de vuelta. +\interaction{branch-repo.bugfix} +Mientras tanto, el desarrollo para la siguiente versión mayor puede +continuar aislado e incólume, en el repositorio \texttt{myproject}. +\interaction{branch-repo.new} + +\section{No repita trabajo: fusión entre ramas} + +En muchos casos, cuando tiene un fallo para arreglar en una rama de +mantenimiento, es muy probable que el fallo también esté en la rama +principal (y posiblemente en otras ramas de mantenimiento +también). Solamente un desarrollador extraño desearía corregir el +mismo fallo muchas veces, por tanto, veremos varias alternativas con +las que Mercurial puede ayudarle a administrar tales arreglos de fallo +sin duplicar su trabajo. + +En el caso más sencillo, basta con jalar los cambios de la rama de +mantenimiento a la rama objetivo en su clon local. +\interaction{branch-repo.pull} +A continuación deberá mezclar las cabezas de las dos ramas, y empujar +de nuevo a la rama principal. +\interaction{branch-repo.merge} + +\section{Nombrar ramas dentro de un repositorio} + +La aproximación correcta en casi todas las oportunidades es aislar las +ramas en los repositorios. Es fácil de entender gracias a su +simplicidad; y es difícil cometer errores. Hay una relación uno a uno +entre las ramas y los directorios con los que está trabajando en su +sistema. Esto le permite usar emplear herramientas usuales (que no son +conscientes de Mercurial) para trabajar con los ficheros dentro de una +rama/repositorio. + +Si se encuentra más en la categoría ``usuario diestro'' (\emph{y} sus +colaboradores también), puede considerar otra alternativa para +administrar las ramas. He mencionado con anterioridad la distinción a +nivel humano entre las ramas estilo ``cuadro pequeño'' y ``gran +cuadro''. Mientras que Mercurial trabaja con muchas ramas del estilo +``cuadro pequeño'' en el repositorio todo el tiempo (por ejemplo cuando +usted jala cambios, pero antes de fusionarlos), \emph{también} puede +trabajar con varias ramas del ``cuadro grande''. + +El truco para trabajar de esta forma en Mercurial se logra gracias a +que puede asignar un \emph{nombre} persistente a una rama. Siempre +existe una rama llamada \texttt{default}. Incluso antes de que +empiece a nombrar ramas por su cuenta, puede encontrar indicios de la +rama \texttt{default} si los busca. + +Por ejemplo, cuando invoca la orden \hgcmd{commit}, y se lanza su +editor para introducir el mensaje de la consignación, busque la línea +que contiene el texto ``\texttt{HG: branch default}'' al final. Le +está indicando que su consignación ocurrirá en la rama llamada +\texttt{default}. + +Use la orden \hgcmd{branches} para empezar a trabajar con ramas +nombradas. Esta orden mostrará las ramas presentes en su repositorio, +indicándole qué conjunto de cambios es la punta de cada una. +\interaction{branch-named.branches} +Dado que todavía no ha creado ramas nombradas, la única que verá será +\texttt{default}. + +Para hallar cuál es la rama ``actual'', invoque la orden +\hgcmd{branch}, sin argumento alguno. Le informará en qué rama se +encuentra el padre del conjunto de cambios actual. +\interaction{branch-named.branch} + +Para crear una nueva rama, invoque la orden \hgcmd{branch} de +nuevo. En esta oportunidad, ofrezca un argumento: el nombre de la rama +que desea crear. +\interaction{branch-named.create} + +Después de crear la rama, usted podría desear ver el efecto que tuvo +la orden \hgcmd{branch}. ¿Qué reportan las ordenes \hgcmd{status} y +\hgcmd{tip}? +\interaction{branch-named.status} +Nada cambia en el directorio actual, y no se ha añadido nada al +historial. Esto sugiere que al ejecutar la orden \hgcmd{branch} no hay +un efecto permanente; solamente le indica a que nombre de rama usará +la \emph{próxima} vez que consigne un conjunto de cambios. + +Cuando consigna un cambio, Mercurial almacena el nombre de la rama en +la cual consignó. Una vez que haya cambiado de la rama \texttt{default} +y haya consignado, verá que el nombre de la nueva rama se mostrará +cuando use la orden \hgcmd{log}, \hgcmd{tip}, y otras órdenes que +desplieguen la misma clase de información. +\interaction{branch-named.commit} +Las órdenes del tipo \hgcmd{log} imprimirán el nombre de la rama de +cualquier conjunto de cambios que no esté en la rama +\texttt{default}. Como resultado, si nunca usa ramas nombradas, nunca +verá esta información. + +Una vez que haya nombrado una rama y consignado un cambio con ese +nombre, todas las consignaciones subsecuentes que desciendan de ese +cambio heredarán el mismo nombre de rama. Puede cambiar el nombre de +una rama en cualquier momento con la orden \hgcmd{branch}. +\interaction{branch-named.rebranch} +Esto es algo que no hará muy seguido en la práctica, debido que los +nombres de las ramas tienden a tener vidas largas. (Esto no es una +regla, solamente una observación.) + +\section{Tratamiento de varias ramas nombradas en un repositorio} + +Si tiene más de una rama nombrada en un repositorio, Mercurial +recordará la rama en la cual está su directorio de trabajo cuando +invoque una orden como \hgcmd{update} o \hgcmdargs{pull}{-u}. Se +actualizará su directorio de trabajo actual a la punta de esta rama, sin +importar cuál sea la punta ``a lo largo del repositorio''. Para +actualizar a una revisión que está en una rama con distinto nombre, +puede necesitar la opción \hgopt{update}{-C} de \hgcmd{update}. + +Este comportamiento puede ser sutil, así que veámoslo en acción. Primero, +recordemos en qué rama estamos trabajando, y qué ramas están en +nuestro repositorio. +\interaction{branch-named.parents} +Estamos en la rama \texttt{bar}, pero existe otra rama más antigua +llamada \hgcmd{foo}. + +Podemos hacer \hgcmd{update} entre los tipos de las ramas \texttt{foo} +y \texttt{bar} sin necesidad de usar la opción \hgopt{update}{-C}, +puesto que esto solamente implica ir linealmente hacia adelante y +atrás en nuestro historial de cambios. +\interaction{branch-named.update-switchy} + +Si volvemos a la rama \texttt{foo} e invocamos la orden \hgcmd{update}, +nos mantendrá en \texttt{foo}, sin movernos a la punta de \texttt{bar}. +\interaction{branch-named.update-nothing} + +Al consignar un cambio a la rama \texttt{foo} se introducirá una nueva +cabeza. +\interaction{branch-named.foo-commit} + +\section{Nombres de ramas y fusiones} + +Posiblemente ha notado que las fusiones en Mercurial no son simétricas. +Supongamos que su repositorio tiene dos cabezas, 17 y 23. Si yo invoco +\hgcmd{update} a 17 y aplico \hgcmd{merge} a 23, Mercurial almacena 17 +como el primer padre de la fusión, y 23 como el segundo. Mientras que +si hago \hgcmd{update} a 23 y después aplico \hgcmd{merge} con 17, +grabará a 23 como el primer padre, y 17 como el segundo. + +Esto afecta el cómo elige Mercurial el nombre de la rama cuando usted +hace la fusión. Después de una fusión, Mercurial mantendrá el nombre de la +rama del primer padre cuando consigne el resultado de la fusión. Si +el primer nombre de su padre es \texttt{foo}, y fusiona con +\texttt{bar}, el nombre de la rama continuará siendo \texttt{foo} +después de fusionar. + +No es inusual que un repositorio contenga varias cabezas, cada una con +el mismo nombre de rama. Digamos que estoy trabajando en la rama +\texttt{foo}, y usted también. Consignamos cambios distintos; yo jalo +sus cambios; Ahora tengo dos cabezas, cada una afirmando estar en la +rama \texttt{foo}. El resultado de una fusión será una única cabeza +en la rama \texttt{foo} como usted esperaría. + +Pero si estoy trabajando en la rama \texttt{bar}, y fusiono el trabajo +de la rama \texttt{foo}, el resultado permanecerá en la rama +\texttt{bar}. +\interaction{branch-named.merge} + +En un ejemplo más concreto, si yo estoy trabajando en la rama +\texttt{bleeding-edge}, y deseo traer los arreglos más recientes de la +rama \texttt{estable}, Mercurial elegirá el nombre de rama ``correcto'' +(\texttt{bleeding-edge}) cuando yo jale una fusión desde \texttt{estable}. + +\section{Normalmente es útil nombrar ramas} + +No debería considerar que las ramas nombradas son aplicables +únicamente en situaciones con muchas ramas de larga vida cohabitando +en un mismo repositorio. Son muy útiles incluso en los casos de +una rama por repositorio. + +En el caso más sencillo, dar un nombre a cada rama ofrece un registro +permanente acerca de en qué conjunto de cambios se generó la rama. +Esto le ofrece más contexto cuando esté tratando de seguir el +historial de un proyecto ramificado de larga vida. + +Si está trabajando con repositorios compartidos, puede configurar el gancho +\hook{pretxnchangegroup} para que cada uno bloquee los cambios con +nombres de rama ``incorrectos'' que están por adicionarse. Este +provee una defensa sencilla, pero efectiva, para evitar que la gente +publique accidentalmente cambios de una rama ``super nueva'' a la rama +``estable''. Tal gancho podría verse de la siguiente forma dentro de +un repositorio compartido de \hgrc. +\begin{codesample2} + [hooks] + pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch +\end{codesample2} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/cmdref.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/cmdref.py \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/cmdref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,186 @@ +\chapter{Referencia de Órdenes} +\label{cmdref} + +\cmdref{add}{Añade ficheros en la próxima consignación} +\optref{add}{I}{include} +\optref{add}{X}{exclude} +\optref{add}{n}{dry-run} + +\cmdref{diff}{imprime los cambios en el historial o el directorio actual} + +Mostrar las diferencias entre revisiones para ficheros especificados o +directorios, con el formato unificado diff. Si desea ver una +descripción del formato unificado diff, ver la sección~\ref{sec:mq:patch}. + +De forma predeterminada, esta orden no imprime las diferencias para +los ficheros binarios que Mercurial esté siguiendo. Para controlar +este comportamiento, vea las opciones \hgopt{diff}{-a} y +\hgopt{diff}{--git}. + +\subsection{Options} + +\loptref{diff}{nodates} + +Omite la fecha y hora cuando se muestran los encabezados de las +diferencias. + +\optref{diff}{B}{ignore-blank-lines} + +No imprime los cambios que solamente insertan o eliminan líneas en +blanco. Una línea que contiene espacios en blanco no se considera +como una línea en blanco. + +\optref{diff}{I}{include} + +Incluye ficheros y directorios cuyos nombres coinciden con los +patrones elegidos. + +\optref{diff}{X}{exclude} + +Excluye los ficheros y directorios cuyos nombres coinciden con los +patrones elegidos. + +\optref{diff}{a}{text} + +Si no especifica esta opción, \hgcmd{diff} no mostrará las diferencias +de los ficheros que detecte como binarios. Al especificar \hgopt{diff}{-a} +se forza a \hgcmd{diff} a tratar los ficheros como texto, y generar +diferencias para todos. + +Esta opción es útil para los ficherso que son ``texto en mayor +medida'' pero que tienen caracteres NUL. Si lo usa en ficheros que +contienen muchos datos binarios, la salida será incomprensible. + +\optref{diff}{b}{ignore-space-change} + +No imprime si el único cambio que en la línea es la cantidad de +espacio en blanco. + +\optref{diff}{g}{git} + +Mostrar diferencias compatibles con \command{git}. XXX reference a format +description. + +\optref{diff}{p}{show-function} + +Mostrar el nombre de la función que contiene el código en una porción +del encabzado usando una heurística simple. Esta funcionalidad se +habilita de forma predeterminada, así que la opción \hgopt{diff}{-p} +no tiene efectos a menos que cambie el valor de +\rcitem{diff}{showfunc} en la configuración, como en el ejemplo +siguiente. +\interaction{cmdref.diff-p} + +\optref{diff}{r}{rev} + +Especifique una o más revisiones para comparar. La orden \hgcmd{diff} +acepta hasta dos opciones \hgopt{diff}{-r} para especificar las +revisiones a comparar. + +\begin{enumerate} +\setcounter{enumi}{0} +\item Despliega las diferencias entre la revisión padre y del directorio + de trabajo. +\item Despliega las diferencias entre el conjunto de cambios + especificados y el directorio de trabajo. +\item Despliega las diferencias entre dos conjuntos de cambios + especificados. +\end{enumerate} + +Puede especificar dos revisiones usando o bien sea las opciones +\hgopt{diff}{-r} o la notación de rango. Por ejemplo, las dos +especificaciones de revisiones a continuación son equivalentes: +\begin{codesample2} + hg diff -r 10 -r 20 + hg diff -r10:20 +\end{codesample2} + +Cuando especifica dos revisiones, esto tiene significado para +Mercurial. Esto significa que \hgcmdargs{diff}{-r10:20} producirá un +diff que transformará los ficheros desde los contenidos en la revisión +10 a los contenidos de la revisión 20, mientras que +\hgcmdargs{diff}{-r20:10} significa lo opuesto: el diff que +transformaría los contenidos de los ficheros de la revisión 20 a los +contenidos de la revisión 10. No puede invertir el orden de esta +forma si está haciendo un diff frente al directorio de trabajo. + +\optref{diff}{w}{ignore-all-space} + +\cmdref{version}{imprime la información de versión y derechos de reproducción} + +Esta orden despliega la versión de Mercurial que está usando, y su +nota de derechos de reproducción. Hay cuatro clases de cadenas de +versión posibles: +\begin{itemize} +\item La cadena ``\texttt{unknown}''. Esta versión de Mercurial no fue + construida en un repositorio de Mercurial, y no puede determinar su + propia versión. +\item Una cadena numérica corta, tal como ``\texttt{1.1}''. Esta es + una construcción de una versión de Mercurial que se identifica con + una etiqueta específica en el repositorio en el cual fue + armada (Esto no significa necesariamente que está ejecutando una + versión oficial; alguien pudo haber añadido tal etiqueta a cualquier + versión del repositorio en el cual armaron Mercurial). +\item Una cadena hexadecimal, tal como ``\texttt{875489e31abe}''. + Esta es una construcción de una revisión dada de Mercurial. +\item Una cadena hexadecimal seguida por una fecha, tal como + ``\texttt{875489e31abe+20070205}''. Esta construcción de la + revisión de Mercurial fue la construcción de un repositorio que tuvo + cambios locales que no han sido consignados. +\end{itemize} + +\subsection{Consejos y trucos} + +\subsubsection{¿Por qué difieren los resultados de \hgcmd{diff} y + \hgcmd{status}?} +\label{cmdref:diff-vs-status} + +Cuando ejecuta la orden \hgcmd{status}, verá una lista de ficheros +para los cuales Mercurial almacenará cambios la próxima vez que +consigne. Si ejecuta la orden \hgcmd{diff}, verá que imprime +diferencias solamente para un \emph{subconjunto} de los ficheros que +\hgcmd{status} liste. Hay dos posibles razones para este comportamiento: + +La primera es que \hgcmd{status} imprime cierta clase de +modificaciones que \hgcmd{diff} no despliega normalmente. La orden +\hgcmd{diff} usualmente despliega diferencias unificadas, las cuales +no tienen la habilidad de representar algunos cambios que Mercurial +puede seguir. Lo más notable es que las diferencias tradicionales no +pueden representar un cambio acerca de la ejecutabilidad de un +fichero, pero Mercurial sí almacena esta información. + +Si usa la opción \hgopt{diff}{--git} de \hgcmd{diff}, mostrará +diferencias compatibles con \command{git} que \emph{pueden} desplegar +esta información adicional. + +La segunda razón posible para que \hgcmd{diff} esté imprimiendo +diferencias para un subconjunto de ficheros de lo que muestra +\hgcmd{status} es que si usted le invoca sin argumento alguno, +\hgcmd{diff} imprime diferencias frente al primer padre del directorio +de trabajo. Si ha ejecutado \hgcmd{merge} para fusionar dos conjuntos +de cambios, pero no ha consignado aún los resultados de la fusión, su +directorio de trabajo tiene dos padres (use \hgcmd{parents} para +verlos). Mientras que \hgcmd{status} imprime modificaciones relativas +a \emph{ambos} padres después de una fusión que no se ha consignado, +\hgcmd{diff} opera aún relativo solamente al primer padre. Puede +lograr que imprima las diferencias relativas al segundo padre +especificando tal padre con la opción \hgopt{diff}{-r}. No hay forma +de hacer que imprima las diferencias relativas a los dos padres. + +\subsubsection{Generar diferencias seguras en binarios} + +Si usa la opción \hgopt{diff}{-a} para forzar que Mercurial imprima +las diferencias de los ficheros que so o bien ``casi completamente +texto'' o contienen muchos datos binarios, tales diferencias no pueden +aplicarse subsecuentemente a la orden \hgcmd{import} de Mercurial o a +la orden \command{patch} del sistema. + +Si desea generar una diferencia de un fichero binario que es seguro +para usarlo como entrada a la orden \hgcmd{import}, use la opción +\hgcmd{diff}{--git} cuando genere el parche. La orden \command{patch} +del sistema no puede tratar con parches binarios. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/collab.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1059 @@ +\chapter{Colaborar con otros} +\label{cha:collab} + +Debido a su naturaleza descentralizada, Mercurial no impone política +alguna de cómo deben trabajar los grupos de personas. Sin embargo, si +usted es nuevo al control distribuido de versiones, es bueno tener +herramientas y ejemplos a la mano al pensar en posibles modelos de +flujo de trabajo. + +\section{La interfaz web de Mercurial} + +Mercurial tiene una poderosa interfaz web que provee bastantes +capacidades útiles. + +Para uso interactivo, la interfaz le permite visualizar uno o varios +repositorios. Puede ver el historial de un repositorio, examinar cada +cambio (comentarios y diferencias), y ver los contenidos de cada +directorio y fichero. + +Adicionalmente la interfaz provee notificaciones RSS de los cambios de los +repositorios. Que le permite ``subscribirse''a un repositorio usando +su herramienta de lectura de notificaciones favorita, y ser notificado +automáticamente de la actividad en el repositorio tan pronto como +sucede. Me gusta mucho más este modelo que el estar suscrito a una +lista de correo a la cual se envían las notificaciones, dado que no +requiere configuración adicional de parte de quien sea que está +administrando el repositorio. + +La interfaz web también permite clonar repositorios a los usuarios +remotos, jalar cambios, y (cuando el servidor está configurado para +permitirlo) publicar cambios en el mismo. El protocolo de entunelamiento +de Mercurial comprime datos agresivamente, de forma que trabaja +eficientemente incluso con conexiones de red con poco ancho de banda. + +La forma más sencilla de iniciarse con la interfaz web es usar su +navegador para visitar un repositorio existente, como por ejemplo el +repositorio principal de Mercurial \url{http://www.selenic.com/repo/hg?style=gitweb}. + +Si está interesado en proveer una interfaz web a sus propios +repositorios, Mercurial provee dos formas de hacerlo. La primera es +usando la orden \hgcmd{serve}, que está enfocada a servir ``de forma +liviana'' y por intervalos cortos. Para más detalles de cómo usar +esta orden vea la sección~\ref{sec:collab:serve} más adelante. Si +tiene un repositorio que desea hacer permanente, Mercurial tiene +soporte embebido del \command{ssh} para publicar cambios con seguridad +al repositorio central, como se documenta en la +sección~\ref{sec:collab:ssh}. Es muy usual que se publique una copia +de sólo lectura en el repositorio que está corriendo sobre HTTP usando +CGI, como en la sección~\ref{sec:collab:cgi}. Publicar sobre HTTP +satisface las necesidades de la gente que no tiene permisos de +publicación y de aquellos que quieren usar navegadores web para +visualizar el historial del repositorio. + +\subsection{Trabajo con muchas ramas} + +Los proyectos de cierta talla tienden naturalmente a progresar de +forma simultánea en varios frentes. En el caso del software, es común +que un proyecto tenga versiones periódicas oficiales. Una versión +puede entrar a ``modo mantenimiento'' por un tiempo después de su +primera publicación; las versiones de mantenimiento tienden a contener +solamente arreglos de fallos, pero no nuevas características. En +paralelo con las versiones de mantenimiento puede haber una o muchas +versiones futuras pueden estar en desarrollo. La gente usa normalmente +la palabra ``rama'' para referirse a una de las direcciones +ligeramente distintas en las cuales procede el desarrollo. + +Mercurial está especialmente preparado para administrar un buen número +de ramas simultáneas pero no idénticas. Cada ``dirección de +desarrollo'' puede vivir en su propio repositorio central, y puede +mezclar los cambios de una a otra de acuerdo con las necesidades. Dado +que los repositorios son independientes, uno del otro, los cambios +inestables de una rama de desarrollo nunca afectarán una rama estable +a menos que alguien explícitamente mezcle los cambios. + +A continuación un ejemplo de cómo podría hacerse esto en la +práctica. Digamos que tiene una ``rama principal'' en un servidor +central. +\interaction{branching.init} +Alguien lo clona, hace cambios locales, los prueba, y los publica allí +mismo. + +Una vez que la rama principal alcanza una estado de versión se puede +usar la orden \hgcmd{tag} para dar un nombre permanente a la revisión. +\interaction{branching.tag} +Digamos que en la rama principal ocurre más desarrollo. +\interaction{branching.main} +Cuando se usa la etiqueta con que se identificó la versión, la gente +puede clonar el repositorio en cualquier momento en el futuro +empleando \hgcmd{update} para obtener una copia del directorio de +trabajo exacta como cuando se creó la etiqueta de la revisión que se +consignó. +\interaction{branching.update} + +Adicionalmente, justo después de que la rama principal se etiquete, +alguien puede clonarla en el servidor a una nueva rama ``estable'', +también en el servidor. +\interaction{branching.clone} + +Alguien que requiera hacer un cambio en la rama estable puede clonar +\emph{ese} repositorio, hacer sus cambios, consignar y publicarlos +posteriormente al inicial. +\interaction{branching.stable} +Puesto que los repositorios de Mercurial son independientes, y que +Mercurial no mueve los cambios de un lado a otro automáticamente, las +ramas estable y principal están \emph{aisladas} la una de la otra. +Los cambios que haga en la rama principal no ``se filtran'' a la rama +estable o vice versa. + +Es usual que los arreglos de fallos de la rama estable deban hacerse +aparecer en la rama principal también. En lugar de reescribir el +arreglo del fallo en la rama principal, puede jalar y mezclar los +cambios de la rama estable a la principal, Mercurial traerá tales +arreglos por usted. +\interaction{branching.merge} +La rama principal contendrá aún los cambios que no están en la +estable y contendrá además todos los arreglos de fallos de la rama +estable. La rama estable permanece incólume a tales cambios. + +\subsection{Ramas de Características} + +En proyectos grandes, una forma efectiva de administrar los cambios es +dividir el equipo en grupos más pequeños. Cada grupo tiene una rama +compartida, clonada de una rama ``principal'' que conforma el proyecto +completo. Aquellos que trabajan en ramas individuales típicamente +están aislados de los desarrollos de otras ramas. + +\begin{figure}[ht] + \centering + \grafix{feature-branches} + \caption{Ramas de Características} + \label{fig:collab:feature-branches} +\end{figure} + +Cuando una rama particular alcanza un estado deseado, alguien del +equipo de características jala y fusiona de la rama principal hacia +la rama de características y publica posteriormente a la rama principal. + +\subsection{El tren de publicación} + +Algunos proyectos se organizan al estilo``tren'': Una versión se +planifica para ser liberada cada cierto tiempo, y las características +que estén listas cuando ha llegado el momento ``tren'', se incorporan. + +Este modelo tiene cierta similitud a las ramas de características. La +diferencia es que cuando una característica pierde el tren, alguien en +el equipo de características jala y fusiona los cambios que se fueron +en la versión liberada hacia la rama de característica, y el trabajo +continúa sobre lo fusionado para que la característica logre estar en +la próxima versión. + +\subsection{El modelo del kernel linux} + +El desarrollo del Kernel Linux tiene una estructura jerárquica +bastante horizontal, rodeada de una nube de caos aparente. Dado que la +mayoría de desarrolladores usan \command{git}, una herramienta distribuida +de control de versiones con capacidades similares a Mercurial, resulta +de utilidad describir la forma en que el trabajo fluye en tal +ambiente; si le gustan las ideas, la aproximación se traduce bien +entre Git y Mercurial. + +En el centro de la comunidad está Linus Torvalds, el creador de Linux. +Él publica un único repositorio que es considerado el árbol +``oficial'' actual por la comunidad completa de +desarrolladores. Cualquiera puede clonar el árbol de Linus, pero él es +muy selectivo acerca de los árboles de los cuales jala. + +Linus tiene varios ``lugartenientes confiables''. Como regla, él jala +todos los cambios que ellos publican, en la mayoría de los casos sin +siquiera revisarlos. Algunos de sus lugartenientes generalmente +aceptan ser los ``mantenedores'', responsables de subsistemas +específicos dentro del kernel. Si un hacker cualquiera desea hacer un +cambio a un subsistema y busca que termine en el árbol de Linus, debe +encontrar quién es el mantenedor del subsistema y solicitarle que +tenga en cuenta su cambio. Si el mantenedor revisa los cambios y está +de acuerdo en tomarlos, estos pasarán al árbol de Linus de acuerdo a +lo expuesto. + +Cada lugarteniente tiene su forma particular de revisar, aceptar y +publicar los cambios; y para decidir cuando hacerlos presentes a +Linus. Adicionalmente existen varias ramas conocidas que mucha gente +usa para propósitos distintos. Por ejemplo, pocas personas mantienen +repositorios ``estables'' de versiones anteriores del kernel, a los +cuales aplican arreglos de fallos críticos necesarios. Algunos +mantenedores publican varios árboles: uno para cambios +experimentales; uno para cambios que van a ofrecer al mantenedor +principal; y así sucesivamente. Otros publican un solo árbol. + +Este modelo tiene dos características notables. La primera es que son +de ``jalar exclusivamente''. Usted debe solicitar, convencer o +incluso rogar a otro desarrollador para que tome sus cambios, porque +casi no hay árboles en los cuales más de una persona pueda publicar, y +no hay forma de publicar cambios en un árbol que otra persona controla. + +El segundo está basado en reputación y meritocracia. Si usted es un +desconocido, Linus probablemente ignorará sus cambios, sin siquiera +responderle. Pero un mantenedor de un subsistema probablemente los +revisara, y los acogerá en caso de que aprueben su criterio de +aplicabilidad. A medida que usted ofrezca ``mejores'' cambios a un +mantenedor, habrá más posibilidad de que se confíe en su juicio y se +acepten los cambios. Si usted es reconocido y mantiene una rama +durante bastante tiempo para algo que Linus no ha aceptado, personas +con intereses similares pueden jalar sus cambios regularmente para +estar al día con su trabajo. + +La reputación y meritocracia no necesariamente es transversal entre +``personas'' de diferentes subsistemas. Si usted es respetado pero es +un hacker en almacenamiento y trata de arreglar un fallo de redes, +tal cambio puede recibir un nivel de escrutinio de un mantenedor de +redes comparable con el que se le haría a un completo extraño. + +Personas que vienen de proyectos con un ordenamiento distinto, sienten +que el proceso comparativamente caótico del Kernel Linux es +completamente lunático. Es objeto de los caprichos individuales; la +gente desecha cambios cuando lo desean; y la fase de desarrollo es +alucinante. A pesar de eso Linux es una pieza de software exitosa y +bien reconocida. + +\subsection{Solamente jalar frente a colaboración pública} + +Una fuente perpetua de discusiones en la comunidad de código abierto +yace en el modelo de desarrollo en el cual la gente solamente jala +cambios de otros ``es mejor que'' uno en el cual muchas personas +pueden publicar cambios a un repositorio compartido. + +Típicamente los partidarios del modelo de publicar usan las herramientas +que se apegan a este modelo. Si usted usa una herramienta +centralizada de control de versiones como Subversion, no hay forma de +elegir qué modelo va a usar: La herramienta le ofrece publicación +compartida, y si desea hacer cualquier otra cosa, va a tener que +aplicar una aproximación artificial (tal como aplicar parches a mano). + +Una buena herramienta distribuida de control de versiones, tal como +Mercurial soportará los dos modelos. Usted y sus colaboradores +pueden estructurar cómo trabajarán juntos basados en sus propias +necesidades y preferencias, sin depender de las peripecias que la +herramienta les obligue a hacer. + +\subsection{Cuando la colaboración encuentra la administración ramificada} + +Una vez que usted y su equipo configurar algunos repositorios +compartidos y comienzan a propagar cambios entre sus repositorios +locales y compartidos, comenzará a encarar un reto relacionado, pero +un poco distinto: Administrar las direcciones en las cuales su equipo +puede moverse. A pesar de que está íntimamente ligado acerca de cómo +interactúa su equipo, es lo suficientemente denso para ameritar un +tratamiento en el capítulo~\ref{chap:branch}. + +\section{Aspectos técnicos de la colaboración} + +Lo que resta del capítulo lo dedicamos a las cuestiones de servir +datos a sus colaboradores. + +\section{Compartir informalmente con \hgcmd{serve}} +\label{sec:collab:serve} + +La orden \hgcmd{serve} de Mercurial satisface de forma espectacular +las necesidades de un grupo pequeño, acoplado y de corto +tiempo. Se constituye en una demostración de cómo se siente usar los +comandos usando la red. + +Ejecute \hgcmd{serve} dentro de un repositorio, y en pocos segundos +iniciará un servidor HTTP especializado; aceptará conexiones desde +cualquier cliente y servirá datos de este repositorio mientrs lo +mantenga funcionando. Todo el que sepa el URL del servidor que ha +iniciado, y que puede comunicarse con su computador por la red, puede +usar un navegador web o Mercurial para leer datos del repositorio. Un +URL para una instancia de \hgcmd{serve} ejecutándose en un portátil +debería lucir algo \Verb|http://my-laptop.local:8000/|. + +La orden \hgcmd{serve} \emph{no} es un servidor web de propósito +general. Solamente puede hacer dos cosas: +\begin{itemize} +\item Permitir que se pueda visualizar el historial del repositorio que + está sirviendo desde navegadores web. +\item Hablar el protocolo de conexión de Mercurial para que puedan hacer + \hgcmd{clone} o \hgcmd{pull} (jalar) cambios de tal repositorio. +\end{itemize} +En particular, \hgcmd{serve} no permitirá que los usuarios remotos +puedan \emph{modificar} su repositorio. Es de tipo solo lectura. + +Si está comenzando con Mercurial, no hay nada que le impida usar +\hgcmd{serve} para servir un repositorio en su propio computador, y +usar posteriormente órdenes como \hgcmd{clone}, \hgcmd{incoming}, para +comunicarse con el servidor como si el repositorio estuviera alojado +remotamente. Lo que además puede ayudarle a adecuarse rápidamente para +usar comandos en repositorios alojados en la red. + +\subsection{Cuestiones adicionales para tener en cuenta} + +Dado que permite lectura sin autenticación a todos sus clientes, +debería usar \hgcmd{serve} exclusivamente en ambientes en los cuáles +no tenga problema en que otros vean, o en los cuales tenga control +completo acerca de quien puede acceder a su red y jalar cambios de su +repositorio. + +La orden \hgcmd{serve} no tiene conocimiento acerca de programas +cortafuegos que puedan estar instalados en su sistema o en su red. No +puede detectar o controlar sus cortafuegos. Si otras personas no +pueden acceder a su instancia \hgcmd{serve}, lo siguiente que debería hacer +(\emph{después} de asegurarse que tienen el URL correcto) es verificar +su configuración de cortafuegos. + +De forma predeterminada, \hgcmd{serve} escucha conexiones entrantes en +el puerto~8000. Si otro proceso está escuchando en tal puerto, usted +podrá especificar un puerto distinto para escuchar con la opción +\hgopt{serve}{-p}. + +Normalmente, cuando se inicia \hgcmd{serve}, no imprime nada, lo cual +puede ser desconcertante. Si desea confirmar que en efecto está +ejecutándose correctamente, y darse cuenta qué URL debería enviar a +sus colaboradores, inícielo con la opción \hggopt{-v}. + +\section{Uso del protocolo Secure Shell (ssh)} +\label{sec:collab:ssh} + +Usted puede publicar y jalar cambios en la red de forma segura usando +el protocolo Secure Shell (\texttt{ssh}). Para usarlo satisfactoriamente, +tendrá que hacer algo de configuración a nivel de cliente o el +servidor. + +Si no está familiarizado con ssh, es un protocolo de red que le permite +comunicarse con seguridad con otro computador. Para usarlo con +Mercurial, estará estableciendo una o más cuentas de usuario en un +servidor de forma tal que los usuarios remotos puedan entrar y +ejecutar órdenes. + +(Si ssh le \emph{es} familiar, encontrará probablemente elemental una +porción del material a continuación.) + +\subsection{Cómo leer y escribir URLs de ssh} + +Los URLs de ssh tienden a lucir de la siguiente forma: +\begin{codesample2} + ssh://bos@hg.serpentine.com:22/hg/hgbook +\end{codesample2} +\begin{enumerate} +\item La parte ``\texttt{ssh://}'' indica a Mercurial que use el + protocolo ssh. +\item El componente ``\texttt{bos@}'' indica el nombre del usuario que + está entrando al servidor. Puede omitirlo si el usuario remoto + coincide con el usuario local. +\item ``\texttt{hg.serpentine.com}'' es el nombre del servidor al cual + se desea entrar. +\item El ``:22'' identifica el número del puerto en el servidor al cual + se conectará. El predeterminado es el~22, así que solamente + necesitará especificar esa porción si \emph{no} está usando el + puerto~22. +\item La última porción del URL es la ruta local al repositorio en el + servidor. +\end{enumerate} + +El componente de la ruta del URL para ssh es una fuente de confusión, +puesto que no hay una forma estándar para que las herramientas puedan +interpretarlo. Algunos programas se comportan de manera distinta a +otros cuando manipulan estas rutas. No es la situación ideal, pero +es muy poco probable que vaya a cambiar. Por favor lea los párrafos +siguientes cuidadosamente. + +Mercurial trata la ruta al repositorio en el servidor como relativo al +directorio personal del usuario remoto. Por ejemplo, si el usuario +\texttt{foo} en el servidor tiene el directorio casa +\dirname{/home/foo}, +entonces un URL ssh que contenga en su ruta a \dirname{bar} +\emph{realmente} se refiere al directorio \dirname{/home/foo/bar}. + +Si desea especificar una ruta relativa a otro directorio de usuario, +puede usar una ruta que comience con un caracter tildado, seguido del +nombre del usuario (llamémosle \texttt{otrousuario}, así +\begin{codesample2} + ssh://server/~otrousuario/hg/repo +\end{codesample2} + +Y si realmente desea especifica una ruta \emph{absoluta} en el +servidor, comience con el componente de la ruta con dos barras como +en el siguiente ejemplo: +\begin{codesample2} + ssh://server//absolute/path +\end{codesample2} + +\subsection{Encontrar un cliente ssh para su sistema} + +Casi todos los sistemas tipo Unix vienen con OpenSSH preinstalado. Si +usted está usando un sistema de estos, ejecute \Verb|which ssh| para +identificar dónde está instalada la orden \command{ssh} (usualmente +estará en \dirname{/usr/bin}). Si por casualidad no está presente, +vea la documentación de sus sistema para lograr instalarlo. + +En Windows, tendrá que escoger primero un cliente adecuado para +descargarlo. Hay dos alternativas: +\begin{itemize} +\item El excelente paquete PuTTY~\cite{web:putty} de Simon Tatham, que + ofrece un suite completo de órdenes de cliente ssh. +\item Si tiene alta tolerancia al dolor, puede usar el porte de Cygwin + para OpenSSH. +\end{itemize} +En cualquier caso, tendrá que editar su fichero \hgini\ para indicarle +a Mercurial dónde encontrar la orden real del cliente. Por ejemplo, si +está usando PuTTY, tendrá que usar la orden \command{plink} como un +cliente de línea de órdenes. +\begin{codesample2} + [ui] + ssh = C:/ruta/a/plink.exe -ssh -i "C:/ruta/a/mi/llave/privada" +\end{codesample2} + +\begin{note} + La ruta a \command{plink} no debería contener espacios o caracteres + en blanco, o Mercurial no podrá encontrarlo correctamente (por lo + tanto, probablemente no sería buena idea colocarlo en + \dirname{C:\\Program Files} +\end{note} + +\subsection{Generar un par de llaves} + +Para evitar la necesidad de teclear una clave de forma repetitiva cada +vez que necesita usar el cliente, recomiendo generar un par de llaves. +En un sistema tipo Unix, la orden \command{ssh-keygen} también se +comportará bien. En Windows, si está usando PuTTY, la orden +\command{puttygen} es la que necesitará. + +Cuando genera un par de llaves, se aconseja \emph{comedidamente} +protegerlas con una frase de clave. (La única oportunidad en la cual +usted querría identificarse una única vez, es cuando está usando +el protocolo ssh para tareas automatizadas en una red segura.) + +No basta con generar un par de llaves. Se requiere adicionar una llave +pública al conjunto de llaves autorizadas para todos los usuarios +remotos que se vayan a autenticar. Para aquellos servidores que usen +OpenSSH (la gran mayoría), significará añadir la llave pública a la +lista en el fichero llamado \sfilename{authorized\_keys} en su +directorio \sdirname{.ssh}. + +En sistemas tipo Unix, su llave pública tendrá la extensión +\filename{.pub}. Si usa \command{puttygen} en Windows, puede +guardar la llave pública en un fichero de su elección, o pegarla desde +la ventana en la cual se despliega directamente en el fichero +\sfilename{authorized\_keys}. + +\subsection{Uso de un agente de autenticación} + +Un agente de autenticación es un demonio que almacena frases clave en +memoria (olvidará las frases clave si sale y vuelve a entrar). Un cliente +ssh notará si está corriendo, y solicitará una frase clave. Si no hay +un agente de autenticación corriendo, o el agente no almacena la frase +clave necesaria, tendrá que teclear su frase clave cada vez que +Mercurial intente comunicarse con un servidor para usted (p.e.~cada vez +que jale o publique cambios). + +El problema de almacenar frases claves en un agente es que es posible +para un atacante bien preparado recuperar el texto plano de su frase +clave, en algunos casos incluso si su sistema sea muy alternante. +Es su decisión si es un riesgo aceptable. Lo que si es seguro es que +evita reteclear. + +En sistemas tipo Unix, el agente se llama \command{ssh-agent}, y +usualmente se ejecuta automáticamente cuando usted entra. Tendrá que +usar la orden \command{ssh-add} para añadir frases claves al agente. En +Windows, si está usando PuTTY, la orden \command{pageant} actúa como +el agente. Añade un icono a su barra del sistema que le permitirá +almacenar frases clave. + +\subsection{Configurar el lado del servidor apropiadamente} + +Dado que puede ser dispendioso configurar ssh si usted es nuevo, hay +una variedad de cosas que podrían ir mal. Añada piense primero en +Mercurial y hay mucho más en qué pensar. La mayor parte de estos +problemas potenciales ocurren en el lado del servidor, no en el cliente. +Las buenas noticias es que una vez tiene una configuración funcional, +usualmente continuará trabajando indefinidamente. + +Antes de intentar que Mercurial hable con un servidor ssh, es mejor +asegurarse que puede usar la orden normal \command{ssh} o \command{putty} +para comunicarse con el servidor primero. Si tiene problemas usando +estas órdenes directamente, de seguro Mercurial no funcionará. Pero aún, +esconderá el problema subyacente. Cuando desee revisar un problema +relacionado con ssh y Mercurial, debería asegurarse primero que las +órdenes de ssh en el lado del cliente funcionan primero, \emph{antes} +de preocuparse por si existe un problema con Mercurial. + +Lo primero para asegurar en el lado del servidor es que puede entrar +desde otra máquina. Si no puede entrar con \command{ssh} o +\command{putty}, el mensaje de error que obtenga le puede dar pistas +de qué ha ido mal. Los problemas más comunes son los siguientes: +\begin{itemize} +\item Si obtiene un error de ``conexión rehusada'', es posible que no + haya un demonio SSH corriendo en el servidor o que no pueda accederse + a él por configuraciones de cortafuegos. +\item Si obtiene un error de ``no hay ruta hasta el servidor'', puede + tener la dirección del servidor incorrecta o un cortafuegos con + bloqueo agresivo que no permitirá su existencia. +\item Si obtiene un mensaje de ``permiso denegado'', puede que haya + tecleado mal el usuario en el servidor, o que haya tecleado + incorrectamente la frase clave o la clave del usuario remoto. +\end{itemize} +En resumen, si tiene problemas al comunicarse con el demonio ssh del +servidor, primero asegúrese de que está corriendo. En muchos sistemas +estará instalado, pero deshabilitado de forma predeterminada. Una vez +que haya hecho este paso tendrá que revisar si el cortafuegos del +servidor está configurado para recibir conexiones entrantes en el +puerto en el cual el demonio de ssh está escuchando (usualmente el~22). +No trate de buscar otras posibilidades exóticas o configuraciones +erradas hasta que haya revisado primero estas dos. + +Si está usando un agente de autenticación en el lado del cliente para +almacenar las frase claves de sus contraseñas, debería poder entrar al +servidor sin necesidad de que se le solicite frases claves o +contraseñas. Si se le pregunta alguna, a continuación algunas +posibilidades: +\begin{itemize} +\item Puede haber olvidado usar \command{ssh-add} o + \command{pageant} para guardar la frase clave. +\item Puede haber almacenado una frase clave errónea para la llave. +\end{itemize} +Si se le solicita la clave del usuario remoto, hay otras posibilidades +que deben revisarse: +\begin{itemize} +\item O bien el directorio del usuario o su directorio \sdirname{.ssh} + tiene permisos excesivamente abiertos. Como resultado el daemonio + ssh no creerá o leerá su fichero \sfilename{authorized\_keys}. + Por ejemplo, un directorio casa o \sdirname{.ssh} causará aveces + este síntoma. +\item El fichero de usuario \sfilename{authorized\_keys} puede tener + un problema. Si alguien distinto al usuario es dueño o puede + escribir el fichero, el demonio ssh no confiará o lo leerá. +\end{itemize} + +En un mundo ideal, debería poder ejecutar la siguiente orden +exitosamente, y debería imprimir exactamente una línea de salida, +la fecha y hora actual. +\begin{codesample2} + ssh miservidor fecha +\end{codesample2} + +Si en su servidor tiene guión que se ejecuta a la entrada e imprime +letreros o cualquier otra cosa, incluso cuando se ejecutan órdenes no +interactivas como esta, debería arreglarlo antes de continuar, de +forma que solamente imprima algo si se ejecuta interactivamente. De +otra forma estos letreros al menos llenarán la salida de Mercurial. +Incluso podrían causar problemas potenciales cuando se ejecuten +órdenes de forma remota. Mercurial intenta detectar e ignorar los +letreros en sesiones no interactivas de \command{ssh}, pero no es +a prueba de tontos. (Si edita sus guiones de entrada en el servidor, +la forma usual de ver si un guión de línea de comandos se ejecuta en un intérprete +interactivo, es verificar el código de retorno de la orden +\Verb|tty -s|.) + +Cuando verifique que el venerado ssh funciona en su servidor, el +paso siguiente es asegurar que Mercurial corre en el servidor. La +orden siguiente debería ejecutarse satisfactoriamente: +\begin{codesample2} + ssh miservidor hg version +\end{codesample2} +Si ve un mensaje de error en lugar de la salida usual de +\hgcmd{version}, será porque no ha instalado Mercurial en +\dirname{/usr/bin}. No se preocupe si este es el caso; no necesita +hacerlo. Pero debería revisar los posibles problemas presentados a +continuación: +\begin{itemize} +\item Está instalado Mercurial en el servidor? Se que suena trivial + pero es mejor revisar! +\item Tal vez la ruta de búsqueda de la interfaz de órdenes + (normalmente vía la variable de ambiente \envar{PATH}) simplemente + está mal configurada. +\item Puede ser que su variable de ambiente \envar{PATH} soalamente + apunte al lugar en el cual está el ejecutable \command{hg} si la + sesión de entrada es interactiva. Puede suceder si establece la + ruta en el guión de línea de comandos de entrada incorrecto. Consulte la + documentación de su línea de órdenes. +\item La variable de ambiente \envar{PYTHONPATH} puede requerir la + ruta a los módulos de Mercurial en Python. Puede que ni siquiera + está establecida; podría estar incorrecta; o puede ser que se + establezca únicamente cuando hay entradas interactivas. +\end{itemize} + +Si puede ejecutar \hgcmd{version} sobre una conexión ssh, +felicitaciones! Ha logrado la interacción entre el cliente y el +servidor. Ahora debería poder acceder a los repositorios de +Mercurial que tiene el usuario en el servidor. Si tiene problemas +con Mercurial y ssh en este punto, intente usar la opción +\hggopt{--debug} para tener información más clara de lo que está +sucediendo. + +\subsection{Compresión con ssh} + +Mercurial no comprime datos cuando usa el protocolo ssh, dado que +el protocolo puede comprimir datos transparentemente. Pero el +comportamiento predeterminado del cliente ssh es \emph{no} +solicitar compresión. + +Sobre cualquier red distinta a una LAN rápida (incluso con una red +inalámbrica), hacer uso de compresión puede mejorar el rendimiento +de las operaciones de Mercurial que involucren la red. Por ejemplo, +sobre WAN, alguien ha medido la compresión reduciendo la cantidad +de tiempo requerido para clonar un repositorio particularmente +grande de~51 minutos a~17 minutos. + +Tanto \command{ssh} como \command{plink} aceptan la opción +\cmdopt{ssh}{-C} que activa la compresión. Puede editar fácilmente +su \hgrc\ para habilitar la compresión para todos los usos de +Mercurial sobre el protocolo ssh. +\begin{codesample2} + [ui] + ssh = ssh -C +\end{codesample2} + +Si usa \command{ssh}, puede reconfigurarlo para que siempre use +compresión cuando se comunique con su servidor. Para hacerlo, +edite su fichero \sfilename{.ssh/config} (que puede no existir +aún), de la siguiente forma: +\begin{codesample2} + Host hg + Compression yes + HostName hg.ejemplo.com +\end{codesample2} +Que define un alias, \texttt{hg}. Cuando lo usa con la orden +\command{ssh} o con una URL de Mercurial con protocolo\texttt{ssh}, +logrará que \command{ssh} se conecte a \texttt{hg.ejemplo.com} +con compresión. Que le dará un nombre más corto para teclear y +compresión, los cuales por derecho propio son buenos. + +\section{Uso de CGI a través de HTTP} +\label{sec:collab:cgi} + +Dependiendo de qué tan ambicioso sea, configurar la interfaz CGI +de Mercurial puede tomar desde unos minutos hasta varias horas. + +Comenzaremos con el ejemplo más sencillo, y nos dirigiremos hacia +configuraciones más complejas. Incluso para el caso más básico +necesitará leer y modificar su configuración del servidor web. + +\begin{note} + Configurar un servidor web es una actividad compleja, engorrosa y + altamente dependiente del sistema. De ninguna manera podremos + cubrir todos los casos posibles con los cuales pueda encontrarse. + Use su discreción y juicio respecto a las secciones siguientes. + Esté preparado para cometer muchas equivocaciones, y emplear + bastante tiempo leyendo sus bitácoras de error del servidor. +\end{note} + +\subsection{Lista de chequeo de la configuración del servidor web} + +Antes de continuar, tómese un tiempo para revisar ciertos aspectos de +la configuración de su sistema: + +\begin{enumerate} +\item ¿Tiene un servidor web? Mac OS X viene con Apache, pero otros + sistemas pueden no tener un servidor web instalado. +\item Si tiene un servidor web instalado, ¿Está ejecutándose? En la + mayoría de sistemas, aunque esté presente, puede no estar habilitado + de forma predeterminada. +\item ¿u servidor está configurado para permitir ejecutar programas + CGI en el directorio donde planea hacerlo? Casi todos los + servidores de forma predeterminada explícitamente inhiben la + habilidad de ejecutar programas CGI. +\end{enumerate} + +Si no tiene un servidor web instalado, y no tiene cierta experiencia +configurando Apache, debería considerar usar el servidor web +\texttt{lighttpd} en lugar de Apache. Apache tiene una reputación +bien ganada por su configuración barroca y confusa. +A pesar de que \texttt{lighttpd} tiene menos características que +Apache en ciertas áreas, las mismas no son relevantes para servir +repositorios de Mercurial. Definitivamente es mucho más sencillo +comenzar con \texttt{lighttpd} que con Apache. + +\subsection{Configuración básica de CGI} + +En sistemas tipo Unix es común que los usuarios tengan un subdirectorio +con un nombre como \dirname{public\_html} en su directorio personal, +desde el cual pueden servir páginas web. Un fichero llamado \filename{foo} +en este directorio será visible en una URL de la forma +\texttt{http://www.example.com/\~username/foo}. + +Para comenzar, encuentre el guión \sfilename{hgweb.cgi} que debería +estar presente en su instalación de Mercurial. Si no puede +encontrarlo rápidamente una copia local en su sistema, puede +descargarlo del repositorio principal de Mercurial en +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi}. + +Tendrá que copiar este guión en su directorio \dirname{public\_html}, +y asegurarse que sea ejecutable. +\begin{codesample2} + cp .../hgweb.cgi ~/public_html + chmod 755 ~/public_html/hgweb.cgi +\end{codesample2} +El argumento \texttt{755} de la orden \command{chmod} es un poco más +general que hacerlo ejecutable: Asegura que el guión sea ejecutable +por cualquiera, y que el ``grupo'' y los ``otros'' \emph{no} tengan +permiso de escritura. Si dejara los permisos de escritura abiertos, +, el subsistema \texttt{suexec} de Apache probablemente se negaría +a ejecutar el guión. De hecho, \texttt{suexec} también insiste en que +el \emph{directorio} en el cual reside el guión no tenga permiso de +escritura para otros. +\begin{codesample2} + chmod 755 ~/public_html +\end{codesample2} + +\subsubsection{¿Qué \emph{podría} resultar mal?} +\label{sec:collab:wtf} + +Cuando haya ubicado el CGI en el sitio correspondiente con un navegador +intente visitar el URL \url{http://myhostname/~myuser/hgweb.cgi}, +\emph{sin} dejarse abatir por un error. Hay una alta probabilidad de +que esta primera visita al URL sea fallida, y hay muchas razones posibles +para este comportamiento. De hecho, podría toparse con cada uno de los +errores que describimos a continuación, así que no deje de leerlos +cuidadosamente. A continuación presento los problemas que yo tuve en +un sistema con Fedora~7, con una instalación nueva de Apache, y una +cuenta de usuario que creé específicamente para desarrollar este +ejercicio. + +Su servidor web puede tener directorios por usuario deshabilitados. Si +usa Apache, busque el fichero de configuración que contenga la +directiva \texttt{UserDir}. Si no está presente en sitio alguno, los +directorios por usuario están deshabilitados. Si la hay, pero su +valor es \texttt{disabled}, los directorios por usuario estarán +deshabilitados. La directiva \texttt{UserDir} en caso contrario tendrá +el nombre del subdirectorio bajo el cual Apache mirará en el +directorio de cada usuario, por ejemplo \dirname{public\_html}. + +Los permisos de sus ficheros pueden ser demasiado restrictivos. El +servidor web debe poder recorrer su directorio personal y los +directorios que estén bajo \dirname{public\_html}, además de tener +permiso para leer aquellos que estén adentro. A continuación una +receta rápida para hacer que sus permisos estén acordes con las +necesidades básicas. +\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} + +Otra posibilidad con los permisos es que obtenga una ventana +completamente en blanco cuando trata de cargar el guión. En cuyo +caso, es posible que los permisos que tiene son \emph{demasiado + permisivos}. El subsistema \texttt{suexec} de Apache no ejecutará un +guión que tenga permisos de escritura para el grupo o el planeta, por +ejemplo. + +Su servidor web puede estar configurado para evitar la ejecución de +programas CGI en los directorios de usuario. A continuación presento +una configuración predeterminada por usuario en mi sistema Fedora. + +\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} +Si encuentra un grupo de instrucciones de \texttt{Directory} similares +en su configuración de Apache, la directiva a revisar es \texttt{Options}. +Adicione \texttt{ExecCGI} al final de esta lista en caso de que haga +falta y reinicie su servidor web. + +Si resulta que Apache le muestra el texto del guión CGI en lugar de +ejecutarlo, necesitará o bien descomentar (si se encuentra presente) o +adicionar una directiva como la siguiente: +\begin{codesample2} + AddHandler cgi-script .cgi +\end{codesample2} + +Otra posibilidad es que observe una traza de Python en colores +informando que no puede importar un módulo relacionado con +\texttt{mercurial}. Esto es un gran progreso! El servidor es capaz +de ejecutar su guión CGI. Este error solamente ocurrirá si está +ejecutando una instalación privada de Mercurial en lugar de una +instalación para todo el sistema. Recuerde que el servidor que +ejecuta el programa CGI no cuenta con variables de ambiente de las +cuales usted si dispone en una sesión interactiva. Si este error le +ocurre, edite su copia de \sfilename{hgweb.cgi} y siga las indicaciones +dentro del mismo para establecer de forma adecuada su variable de +ambiente \envar{PYTHONPATH}. + +Finalmente, si encuentra \emph{otra} traza a todo color de Python al visitar +el URL: Esta seguramente se referirá a que no puede encontrar +\dirname{/path/to/repository}. Edite su script \sfilename{hgweb.cgi} +y reemplace la cadena \dirname{/path/to/repository} con la ruta +completa al repositorio que desea servir. + +En este punto, cuando trate de recargar la página, deberá visualizar +una linda vista HTML del historial de su repositorio. Uff! + +\subsubsection{Configuración de lighttpd} + +En mi intención de ser exhaustivo, intenté configurar +\texttt{lighttpd}, un servidor web con creciente aceptación, para +servir los repositorios de la misma forma como lo describí +anteriormente con Apache. Después de superar los problemas que mostré +con Apache, muchos de los cuáles no son específicos del servidor. Por +lo tanto estaba seguro de que mis permisos para directorios y ficheros +eran correctos y que mi guión \sfilename{hgweb.cgi} también lo era. + +Dado que ya Apache estaba en ejecución correctamente, lograr que +\texttt{lighttpd} sirviera mi repositorio fue rápido (en otras +palabras, si está tratando de usar \texttt{lighttpd}, debe leer la +sección de Apache). Primero tuve que editar la sección +\texttt{mod\_access} para habilitar \texttt{mod\_cgi} y +\texttt{mod\_userdir}, los cuales estaban inhabilitados en mi +instalación predeterminada. Añadí posteriormente unas líneas al final +del fichero de configuración, para hacer lo propio con los módulos. +\begin{codesample2} + userdir.path = "public_html" + cgi.assign = ( ".cgi" => "" ) +\end{codesample2} +Hecho esto, \texttt{lighttpd} funcionó inmediatamente para +mí. Configuré \texttt{lighttpd} antes que Apache, tuve casi los mismos +reparos a nivel de configuración del sistema que con Apache. De todas +maneras, considero que \texttt{lighttpd} es bastante más sencillo de +configurar que Apache, a pesar de haber usado Apache por lo menos por +una década, y esta fue mi primera experiencia con \texttt{lighttpd}. + +\subsection{Compartir varios repositorios con un guión CGI} + +El guión \sfilename{hgweb.cgi} permite publicar únicamente un +repositorio, una restricción frustrante. Si desea publicar más de uno +sin complicarse con varias copias del mismo guión, cada una con un +nombre distinto, resulta mucho mejor usar el guión +\sfilename{hgwebdir.cgi}. + +El procedimiento para configurar \sfilename{hgwebdir.cgi} tiene una +porción adicional frente al trabajo requerido con +\sfilename{hgweb.cgi}. Primero se debe obtener una copia del +guión. Si no tiene una a mano, puede descargar una copia del ftp +principal del repositorio de Mercurial en +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi}. + +Necesitará una copia del guión en su directorio \dirname{public\_html}, +y asegurarse de que sea ejecutable. +\begin{codesample2} + cp .../hgwebdir.cgi ~/public_html + chmod 755 ~/public_html ~/public_html/hgwebdir.cgi +\end{codesample2} +Con la configuración básica, intente visitar en su navegador +\url{http://myhostname/~myuser/hgwebdir.cgi}. Debería mostrar una +lista vacía de repositorios. Si obtiene una ventana en blanco o un +mensaje de error, verifique la lista de problemas potenciales en la +sección~\ref{sec:collab:wtf}. + +El guión \sfilename{hgwebdir.cgi} se apoya en un fichero externo de +configuración. En principio, busca un fichero llamado +\sfilename{hgweb.config} en el mismo directorio. Tendrá que crear el +fichero, y permitir lectura de todo el mundo. El formato del fichero +es similar a un fichero ``ini'' de Windows, que puede interpretar el módulo +\texttt{ConfigParser}~\cite{web:configparser} de Python. + +La forma más sencilla de configurar \sfilename{hgwebdir.cgi} es con +una sección llamada \texttt{collections}. Esta publicará automáticamente +\emph{todos} los repositorios en los directorios que usted +especifique. La sección debería lucir así: +\begin{codesample2} + [collections] + /mi/ruta = /mi/ruta +\end{codesample2} +Mercurial lo interpreta buscando el nombre del directorio que esté a la +\emph{derecha} del símbolo ``\texttt{=}''; encontrando repositorios en +la jerarquía de directorios; y usando el texto a la \emph{izquierda} +para eliminar el texto de los nombres que mostrará en la interfaz +web. El componente restante de la ruta después de esta eliminación +usualmente se llama ``ruta virtual''. + +Dado el ejemplo de arriba, si tenemos un repositorio cuya ruta local es +\dirname{/mi/ruta/este/repo}, el guión CGI eliminará la porción inicial +\dirname{/mi/ruta} del nombre y publicará el repositorio con una ruta +virtual \dirname{este/repo}. Si el URL base de nuestro guión CGI es +\url{http://myhostname/~myuser/hgwebdir.cgi}, el URL completo al +repositorio será +\url{http://myhostname/~myuser/hgwebdir.cgi/this/repo}. + +Si reemplazamos \dirname{/mi/ruta} en el lado izquierdo de este +ejemplo con \dirname{/mi}, \sfilename{hgwebdir.cgi} eliminará solamente +\dirname{/mi} del nombre del repositorio, y nos ofrecerá la ruta +virtual \dirname{ruta/este/repo} en lugar de \dirname{este/repo}. + +El guión \sfilename{hgwebdir.cgi} buscará recursivamente en cada +directorio listado en la sección \texttt{collections} de su fichero de +configuración, pero \texttt{no} hará el recorrido recursivo dentro de +los repositorios que encuentre. + +El mecanismo de \texttt{collections} permite publicar fácilmente +repositorios de una forma ``hacer y olvidar''. Solamente requiere +configurar el guión CGI y el fichero de configuración una vez. +Después de eso puede publicar y sacar de publicación un repositorio en +cualquier momento incluyéndolo o excluyéndolo de la jerarquía de +directorios en la cual le haya indicado a \sfilename{hgwebdir.cgi} que +mirase. + +\subsubsection{Especificación explícita de los repositorios a publicar} + +Además del mecanismo \texttt{collections}, el guión +\sfilename{hgwebdir.cgi} le permite publicar una lista específica de +repositorios. Para hacerlo, cree una sección \texttt{paths}, con los +contenidos de la siguiente forma: +\begin{codesample2} + [paths] + repo1 = /mi/ruta/a/un/repo + repo2 = /ruta/a/otro/repo +\end{codesample2} +En este caso, la ruta virtual (el componente que aparecerá en el URL) +está en el lado derecho de cada definición, mientras que la ruta al +repositorio está a la derecha. Note que no tiene que haber relación +alguna entre la ruta virtual que elija y el lugar del repositorio en +su sistema de ficheros. + +Si lo desea, puede usar los dos mecanismos \texttt{collections} y +\texttt{paths} simultáneamente en un sólo fichero de configuración. + +\begin{note} + Si varios repositorios tienen la misma ruta virtual, + \sfilename{hgwebdir.cgi} no reportará error. Pero se comportará + impredeciblemente. +\end{note} + +\subsection{Descarga de ficheros fuente} + +La interfaz web de Mercurial permite a los usuarios descargar +un conjunto de cualquier revisión. Este fichero contendrá una réplica +del directorio de trabajo en la revisión en cuestión, pero no +contendrá una copia de los datos del repositorio. + +De forma predeterminada esta característica no está habilitada. Para +lograrlo adicione un \rcitem{web}{allow\_archive} a la sección \rcsection{web} +de su fichero \hgrc. + +\subsection{Opciones de configuración en Web} + +Las interfaces web de Mercurial (la orden \hgcmd{serve}, y los guiones +\sfilename{hgweb.cgi} y \sfilename{hgwebdir.cgi}) tienen varias +opciones de configuración para establecer. Todas ellas en la sección +\rcsection{web}. +\begin{itemize} +\item[\rcitem{web}{allow\_archive}] Determina cuáles tipos de ficheros + de descarga soportará Mercurial. Si habilita esta característica, + los usuarios de la interfaz web podrán descargar una copia de la + revisión del repositorio que estén viendo. Para activar la + característica de descarga de fichero, el valor tendrá una secuencia + de palabras extraídas de la lista de abajo. + \begin{itemize} + \item[\texttt{bz2}] Un fichero \command{tar} con el método de + compresión \texttt{bzip2}. Tiene la mejor tasa de compresión, + pero usa más tiempo de procesamiento en el servidor. + \item[\texttt{gz}] Un fichero \command{tar}, comprimido con + \texttt{gzip}. + \item[\texttt{zip}] Un fichero \command{zip}, comprimido con LZW. + Este formato posee la peor tasa de compresión, pero es muy usado en + el mundo Windows. + \end{itemize} + Si da una lista vacía o no tiene la entrada + \rcitem{web}{allow\_archive}, esta característica se deshabilitará. + A continuación un ejemplo de cómo habilitar los tres formatos soportados. + \begin{codesample4} + [web] + allow_archive = bz2 gz zip + \end{codesample4} +\item[\rcitem{web}{allowpull}] Booleano. Determina si la interfaz web + permite a los usuarios remotos emplear \hgcmd{pull} y \hgcmd{clone} + sobre el repositorio~HTTP. Si se coloca \texttt{no} o + \texttt{false}, solamente la porción de los procesos + ``orientados-a-humanos'' se habilita de la interfaz web. +\item[\rcitem{web}{contact}] Cadena. Una cadena en forma libre (pero + preferiblemente corta) que identifica a la persona o grupo a cargo + del repositorio. Usualmente contiene el nombre y la dirección de + correo electrónico de una persona o de una lista de correo. Aveces + tiene sentido colocar esta opción en el fichero \sfilename{.hg/hgrc} + del repositorio, pero en otras oportunidades en el \hgrc\ global si + todos los repositorios tienen un único mantenedor. +\item[\rcitem{web}{maxchanges}] Entero. La cantidad máxima de + conjuntos de cambios a mostrar de forma predeterminada en cada página. +\item[\rcitem{web}{maxfiles}] Entero. La cantidad máxima + predeterminada de ficheros modificados a desplegar en una página. +\item[\rcitem{web}{stripes}] Entero. Si la interfaz web despliega + ``franjas'' para facilitar la visualización alineada de filas cuando + se ve una tabla, este valor controla la cantidad de filas en cada + franja. +\item[\rcitem{web}{style}] Controla la plantilla que Mercurial usa para + desplegar la interfaz web. Mercurial viene con dos plantillas web, + llamadas \texttt{default} y \texttt{gitweb} (La primera es + visualmente más atractiva). Puede especificar una plantilla propia; + consulte el capítulo~\ref{chap:template}. A continuación mostramos + cómo habilitar el estilo \texttt{gitweb}. + \begin{codesample4} + [web] + style = gitweb + \end{codesample4} +\item[\rcitem{web}{templates}] Ruta. Directorio en el que se buscarán + los ficheros plantilla. De forma predeterminada, busca en el + directorio en el cual fue instalado. +\end{itemize} +Si usa \sfilename{hgwebdir.cgi}, puede añadir otras opciones de +configuración en una sección \section{web} del fichero +\sfilename{hgweb.config} en lugar del fichero \hgrc\, si lo considera +más conveniente. Estas opciones son \rcitem{web}{motd} y +\rcitem{web}{style}. + +\subsubsection{Opciones específicas para repositorios individuales} + +Ciertas opciones de configuración de \rcsection{web} deben estar +ubicadas en el \sfilename{.hg/hgrc} de un repositorio en lugar del +fichero del usuario o el \hgrc global. +\begin{itemize} +\item[\rcitem{web}{description}] Cadena. Una cadena de forma + libre (preferiblemente corta) que describa los contenidos o el + propósito del repositorio. +\item[\rcitem{web}{name}] Cadena. El nombre para visualizar en la + interfaz web del repositorio. Sustituye el nombre predeterminado, el + cual es el último componente de la ruta del repositorio. +\end{itemize} + +\subsubsection{Opciones específicas a la orden \hgcmd{serve}} + +Algunas opciones en la sección \rcsection{web} de un fichero \hgrc\ +son de uso exclusivo para la orden \hgcmd{serve}. +\begin{itemize} +\item[\rcitem{web}{accesslog}] Ruta. El nombre del fichero en el cual + se escribe la bitácora de acceso. En principio, la orden + \hgcmd{serve} escribe esta información a la salida estándar, no a un + fichero. Las líneas de la bitácora se escriben en un formato de + fichero ``combinado'' estándar, usado por casi todos los servidores + web. +\item[\rcitem{web}{address}] Cadena. La dirección local en la cual el + servidor debe escuchar peticiones entrantes. En principio, el + servidor escucha en todas las direcciones. +\item[\rcitem{web}{errorlog}] Ruta. El nombre de un fichero en el + cual escribir la bitácora de error. En principio, la orden + \hgcmd{serve} escribe esta información en la salida de error + estándar, no a un fichero. +\item[\rcitem{web}{ipv6}] Booleano. Si se usa o no el protocolo + IPv6. En principio, IPv6 no se usa. +\item[\rcitem{web}{port}] Entero. El número del puerto~TCP en el cuál + el servidor escuchará. El puerto predeterminado es el~8000. +\end{itemize} + +\subsubsection{Elegir el fichero \hgrc\ correcto para las + configuraciones de \rcsection{web}} + +Es importante recordar que un servidor web como Apache o +\texttt{lighttpd} se ejecutarán bajo el usuario~ID que generalmente no +es el suyo Los guiones CGI ejecutados por su servidor, tales como +\sfilename{hgweb.cgi}, se ejecutarán también con el usuario~ID. + +Si añade opciones \rcsection{web} a su fichero personal \hgrc\, Los +guiones CGI no leerán tal fichero \hgrc\. Tales configuraciones +solamente afectarán el comportamiento de la orden \hgcmd{serve} cuando +usted lo ejecuta. Para logar que los guiones CGI vean sus +configuraciones, o bien cree un fichero \hgrc\ en el directorio hogar +del usuario ID que ejecuta su servidor web, o añada tales +configuraciones al fichero global \hgrc. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/concepts.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/concepts.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,650 @@ +\chapter{Tras bambalinas} +\label{chap:concepts} + +A diferencia de varios sistemas de control de revisiones, los +conceptos en los que se fundamenta Mercurial son lo suficientemente +simples como para entender fácilmente cómo funciona el software. +Saber esto no es necesario, pero considero útil tener un ``modelo +mental'' de qué es lo que sucede. + +Comprender esto me da la confianza de que Mercurial ha sido +cuidadosamente diseñado para ser tanto \emph{seguro} como +\emph{eficiente}. Y tal vez con la misma importancia, si es fácil +para mí hacerme a una idea adecuada de qué está haciendo el software +cuando llevo a cabo una tarea relacionada con control de revisiones, +es menos probable que me sosprenda su comportamiento. + +En este capítulo, cubriremos inicialmente los conceptos centrales +del diseño de Mercurial, y luego discutiremos algunos detalles +interesantes de su implementación. + +\section{Registro del historial de Mercurial} + +\subsection{Seguir el historial de un único fichero} + +Cuando Mercurial sigue las modificaciones a un fichero, guarda el +historial de dicho fichero en un objeto de metadatos llamado +\emph{filelog}\ndt{Fichero de registro}. Cada entrada en el fichero +de registro contiene suficiente información para reconstruir una +revisión del fichero que se está siguiendo. Los ficheros de registro +son almacenados como ficheros el el directorio +\sdirname{.hg/store/data}. Un fichero de registro contiene dos tipos +de información: datos de revisiones, y un índice para ayudar a +Mercurial a buscar revisiones eficientemente. + +El fichero de registro de un fichero grande, o con un historial muy +largo, es guardado como ficheros separados para datos (sufijo +``\texttt{.d}'') y para el índice (sufijo ``\texttt{.i}''). Para +ficheros pequeños con un historial pequeño, los datos de revisiones y +el índice son combinados en un único fichero ``\texttt{.i}''. La +correspondencia entre un fichero en el directorio de trabajo y el +fichero de registro que hace seguimiento a su historial en el +repositorio se ilustra en la figura~\ref{fig:concepts:filelog}. + +\begin{figure}[ht] + \centering + \grafix{filelog} + \caption{Relación entre ficheros en el directorio de trabajo y + ficheros de registro en el repositorio} + \label{fig:concepts:filelog} +\end{figure} + +\subsection{Administración de ficheros monitoreados} + +Mercurial usa una estructura llamada \emph{manifiesto} para +% TODO collect together => centralizar +centralizar la información que maneja acerca de los ficheros que +monitorea. Cada entrada en el manifiesto contiene información acerca +de los ficheros involucrados en un único conjunto de cambios. Una +entrada registra qué ficheros están presentes en el conjunto de +cambios, la revisión de cada fichero, y otros cuantos metadatos del +mismo. + +\subsection{Registro de información del conjunto de cambios} + +La \emph{bitácora de cambios} contiene información acerca de cada +conjunto de cambios. Cada revisión indica quién consignó un cambio, el +comentario para el conjunto de cambios, otros datos relacionados con +el conjunto de cambios, y la revisión del manifiesto a usar. + +\subsection{Relaciones entre revisiones} + +Dentro de una bitácora de cambios, un manifiesto, o un fichero de +registro, cada revisión conserva un apuntador a su padre inmediato +(o sus dos padres, si es la revisión de una fusión). Como menciońe +anteriormente, también hay relaciones entre revisiones \emph{a través} +de estas estructuras, y tienen naturaleza jerárquica. + +Por cada conjunto de cambios en un repositorio, hay exactamente una +revisión almacenada en la bitácora de cambios. Cada revisión de la +bitácora de cambios contiene un apuntador a una única revisión del +manifiesto. Una revisión del manifiesto almacena un apuntador a una +única revisión de cada fichero de registro al que se le hacía +seguimiento cuando fue creado el conjunto de cambios. Estas relaciones +se ilustran en la figura~\ref{fig:concepts:metadata}. + +\begin{figure}[ht] + \centering + \grafix{metadata} + \caption{Relaciones entre metadatos} + \label{fig:concepts:metadata} +\end{figure} + +Como lo muestra la figura, \emph{no} hay una relación ``uno a uno'' +entre las revisiones en el conjunto de cambios, el manifiesto, o el +fichero de registro. Si el manifiesto no ha sido modificado de un +conjunto de cambios a otro, las entradas en la bitácora de cambios +para esos conjuntos de cambios apuntarán a la misma revisión del +manifiesto. Si un fichero monitoreado por Mercurial no sufre ningún +cambio de un conjunto de cambios a otro, la entrada para dicho fichero +en las dos revisiones del manifiesto apuntará a la misma revisión de +su fichero de registro. + +\section{Almacenamiento seguro y eficiente} + +La base común de las bitácoras de cambios, los manifiestos, y los +ficheros de registros es provista por una única estructura llamada el +\emph{revlog}\ndt{Contracción de \emph{revision log}, registro de +revisión.}. + +\subsection{Almacenamiento eficiente} + +El revlog provee almacenamiento eficiente de revisiones por medio del +mecanismo de \emph{deltas}\ndt{Diferencias.}. En vez de almacenar una +copia completa del fichero por cada revisión, almacena los cambios +necesarios para transformar una revisión anterior en la nueva +revisión. Para muchos tipos de fichero, estos deltas son típicamente +de una fracción porcentual del tamaño de una copia completa del +fichero. + +Algunos sistemas de control de revisiones obsoletos sólo pueden +manipular deltas de ficheros de texto plano. Ellos o bien almacenan +los ficheros binarios como instantáneas completas, o codificados en +alguna representación de texto plano adecuada, y ambas alternativas +son enfoques que desperdician bastantes recursos. Mercurial puede +manejar deltas de ficheros con contenido binario arbitrario; no +necesita tratar el texto plano como un caso especial. + +\subsection{Operación segura} +\label{sec:concepts:txn} + +Mercurial sólo \emph{añade} datos al final de los ficheros de revlog. Nunca +modifica ninguna sección de un fichero una vez ha sido escrita. Esto es más +robusto y eficiente que otros esquemas que requieren modificar o reescribir +datos. + +Adicionalmente, Mercurial trata cada escritura como parte de una +\emph{transacción}, que puede cubrir varios ficheros. Una transacción es +\emph{atómica}: o bien la transacción tiene éxito y entonces todos sus efectos +son visibles para todos los lectores, o la operación completa es cancelada. +% TODO atomicidad no existe de acuerdo a DRAE, reemplazar +Esta garantía de atomicidad implica que, si usted está ejecutando dos copias de +Mercurial, donde una de ellas está leyendo datos y la otra los está escribiendo, +el lector nunca verá un resultado escrito parcialmente que podría confundirlo. + +El hecho de que Mercurial sólo hace adiciones a los ficheros hace más fácil +proveer esta garantía transaccional. A medida que sea más fácil hacer +operaciones como ésta, más confianza tendrá usted en que sean hechas +correctamente. + +\subsection{Recuperación rápida de datos} + +Mercurial evita ingeniosamente un problema común a todos los sistemas de control +de revisiones anteriores> el problema de la +\emph{recuperación\ndt{\emph{Retrieval}. Recuperación en el sentido de traer los +datos, o reconstruirlos a partir de otros datos, pero no debido a una falla o +calamidad, sino a la operación normal del sistema.} ineficiente de datos}. +Muchos sistemas de control de revisiones almacenan los contenidos de una +revisión como una serie incremental de modificaciones a una ``instantánea''. +Para reconstruir una versión cualquiera, primero usted debe leer la instantánea, +y luego cada una de las revisiones entre la instantánea y su versión objetivo. +Entre más largo sea el historial de un fichero, más revisiones deben ser leídas, +y por tanto toma más tiempo reconstruir una versión particular. + +\begin{figure}[ht] + \centering + \grafix{snapshot} + \caption{Instantánea de un revlog, con deltas incrementales} + \label{fig:concepts:snapshot} +\end{figure} + +La innovación que aplica Mercurial a este problema es simple pero efectiva. +Una vez la cantidad de información de deltas acumulada desde la última +instantánea excede un umbral fijado de antemano, se almacena una nueva +instantánea (comprimida, por supuesto), en lugar de otro delta. Esto hace +posible reconstruir \emph{cualquier} versión de un fichero rápidamente. Este +enfoque funciona tan bien que desde entonces ha sido copiado por otros sistemas +de control de revisiones. + +La figura~\ref{fig:concepts:snapshot} ilustra la idea. En una entrada en el +fichero índice de un revlog, Mercurial almacena el rango de entradas (deltas) +del fichero de datos que se deben leer para reconstruir una revisión en +particular. + +\subsubsection{Nota al margen: la influencia de la compresión de vídeo} + +Si le es familiar la compresión de vídeo, o ha mirado alguna vez una emisión de +TV a través de cable digital o un servicio de satélite, puede que sepa que la +mayor parte de los esquemas de compresión de vídeo almacenan cada cuadro del +mismo como un delta contra el cuadro predecesor. Adicionalmente, estos esquemas +usan técnicas de compresión ``con pérdida'' para aumentar la tasa de +compresión, por lo que los errores visuales se acumulan a lo largo de una +cantidad de deltas inter-cuadros. + +Ya que existe la posibilidad de que un flujo de vídeo se ``pierda'' +ocasionalmente debido a fallas en la señal, y para limitar la acumulación de +errores introducida por la compresión con pérdidas, los codificadores de vídeo +insertan periódicamente un cuadro completo (también llamado ``cuadro clave'') en +el flujo de vídeo; el siguiente delta es generado con respecto a dicho cuadro. +Esto quiere decir que si la señal de vídeo se interrumpe, se reanudará una vez +se reciba el siguiente cuadro clave. Además, la acumulación de errores de +codificación se reinicia con cada cuadro clave. + +\subsection{Identificación e integridad fuerte} + +Además de la información de deltas e instantáneas, una entrada en un +% TODO de pronto aclarar qué diablos es un hash? +revlog contiene un hash criptográfico de los datos que representa. +Esto hace difícil falsificar el contenido de una revisión, y hace +fácil detectar una corrupción accidental. + +Los hashes proveen más que una simple revisión de corrupción: son +usados como los identificadores para las revisiones. +% TODO no entendí completamente la frase a continuación +Los hashes de +identificación de conjuntos de cambios que usted ve como usuario final +son de las revisiones de la bitácora de cambios. Aunque los ficheros +de registro y el manifiesto también usan hashes, Mercurial sólo los +usa tras bambalinas. + +Mercurial verifica que los hashes sean correctos cuando recupera +revisiones de ficheros y cuando jala cambios desde otro repositorio. +Si se encuentra un problema de integridad, Mercurial se quejará y +detendrá cualquier operación que esté haciendo. + +Además del efecto que tiene en la eficiencia en la recuperación, el +uso periódico de instantáneas de Mercurial lo hace más robusto frente +a la corrupción parcial de datos. Si un fichero de registro se +corrompe parcialmente debido a un error de hardware o del sistema, a +menudo es posible reconstruir algunas o la mayoría de las revisiones a +partir de las secciones no corrompidas del fichero de registro, tanto +antes como después de la sección corrompida. Esto no sería posible con +un sistema de almacenamiento basado únicamente en deltas. + +\section{Historial de revisiones, ramas y fusiones} + +Cada entrada en el revlog de Mercurial conoce la identidad de la +revisión de su ancestro inmediato, al que se conoce usualmente como su +\emph{padre}. De hecho, una revisión contiene sitio no sólo para un +padre, sino para dos. Mercurial usa un hash especial, llamado el +``ID nulo'', para representar la idea de ``no hay padre aquí''. Este +hash es simplemente una cadena de ceros. + +En la figura~\ref{fig:concepts:revlog} usted puede ver un ejemplo de +la estructura conceptual de un revlog. Los ficheros de registro, +manifiestos, y bitácoras de cambios comparten la misma estructura; +sólo difieren en el tipo de datos almacenados en cada delta o +instantánea. + +La primera revisión en un revlog (al final de la imagen) tiene como +padre al ID nulo, en las dos ranuras disponibles para padres. En una +revisión normal, la primera ranura para padres contiene el ID de la +revisión padre, y la segunda contiene el ID nulo, señalando así que la +revisión sólo tiene un padre real. Un par de revisiones que tenga el +mismo ID padre son ramas. Una revisión que representa una fusión entre +ramas tiene dos IDs de revisión normales en sus ranuras para padres. + +\begin{figure}[ht] + \centering + \grafix{revlog} + \caption{} + \label{fig:concepts:revlog} +\end{figure} + +\section{El directorio de trabajo} + +% TODO revisar párrafo, no me convence la traducción +En el directorio de trabajo, Mercurial almacena una instantánea de los +ficheros del repositorio como si fueran los de un conjunto de cambios +particular. + +El directorio de trabajo ``sabe'' qué conjunto de cambios contiene. +Cuando usted actualiza el directorio de trabajo para que contenga un +conjunto de cambios particular, Mercurial busca la revisión adecuada +del manifiesto para averiguar qué ficheros estaba monitoreando cuando +se hizo la consignación del conjunto de cambios, y qué revisión de +cada fichero era la actual en ese momento. Luego de eso, recrea una +copia de cada uno de esos ficheros, con los mismos contenidos que +tenían cuando fue consignado el conjunto de cambios. + +El \emph{estado de directorio}\ndt{dirstate, en inglés en el +original.} contiene el conocimiento de Mercurial acerca del directorio +de trabajo. Allí se detalla a qué conjunto de cambios es actualizado +el directorio de trabajo, y todos los ficheros que Mercurial está +monitoreando en este directorio. + +Tal como la revisión de un revlog tiene espacio para dos padres, para +que pueda representar tanto una revisión normal (con un solo padre) o +una fusión de dos revisiones anteriores, el estado de directorio tiene +espacio para dos padres. Cuando usted usa el comando \hgcmd{update}, +el conjunto de cambios al que usted se actualiza es almacenado en la +casilla destinada al ``primer padre'', y un ID nulo es almacenado en +la segunda. Cuando usted hace una fusión (\hgcmd{merge}) con otro +conjunto de cambios, la casilla para el primer padre permanece sin +cambios, y la casilla para el segundo es actualizada con el conjunto +de cambios con el que usted acaba de hacer la fusión. El comando +\hgcmd{parents} le indica cuáles son los padres del estado de +directorio. + +\subsection{Qué pasa en una consignación} + +El estado de directorio almacena información sobre los padres para +algo más que mero registro. Mercurial usa los padres del estado de +directorio como \emph{los padres de un nuevo conjunto de cambios} +cuando usted hace una consignación. + +\begin{figure}[ht] + \centering + \grafix{wdir} + \caption{El directorio de trabajo puede tener dos padres} + \label{fig:concepts:wdir} +\end{figure} + +La figura~\ref{fig:concepts:wdir} muestra el estado normal del +directorio de trabajo, que tiene un único conjunto de cambios como +padre. Dicho conjunto de cambios es la \emph{punta}, el conjunto de +cambios más reciente en el repositorio que no tiene hijos. + +\begin{figure}[ht] + \centering + \grafix{wdir-after-commit} + \caption{El directorio de trabajo obtiene nuevos padres luego de una + consignación} + \label{fig:concepts:wdir-after-commit} +\end{figure} + +Es útil pensar en el directorio de trabajo como en ``el conjunto de +cambios que estoy a punto de enviar''. Cualquier fichero que usted le +diga a Mercurial que fue añadido, borrado, renombrado o copiado, se +verá reflejado en ese conjunto de cambios, como también se verán las +modificaciones a cualquiera de los ficheros que Mercurial ya esté +monitoreando; el nuevo conjunto de cambios dentrá los padres del +directorio de trabajo como propios. + +Luego de una consignación, Mercurial actualizará los padres del +directorio de trabajo, de tal manera que el primer padre sea el ID del +nuevo conjunto de cambios, y el segundo sea el ID nulo. Esto puede +verse en la figura~\ref{fig:concepts:wdir-after-commit}. Mercurial no +toca ninguno de los ficheros del directorio de trabajo cuando usted +hace la consignación; sólo modifica el estado de directorio para +anotar sus nuevos padres. + +\subsection{Creación de un nuevo frente} + +Es perfectamente normal actualizar el directorio de trabajo a un +conjunto de cambios diferente a la punta actual. Por ejemplo, usted +podría desear saber en qué estado se encontraba su proyecto el martes +pasado, o podría estar buscando en todos los conjuntos de cambios para +saber cuándo se introdujo un fallo. En casos como éstos, la acción +natural es actualizar el directorio de trabajo al conjunto de cambios +de su interés, y examinar directamente los ficheros en el directorio +de trabajo para ver sus contenidos tal como estaban en el momento de +hacer la consignación. El efecto que tiene esto se muestra en la +figura~\ref{fig:concepts:wdir-pre-branch}. + +\begin{figure}[ht] + \centering + \grafix{wdir-pre-branch} + \caption{El directorio de trabajo, actualizado a un conjunto de + cambios anterior} + \label{fig:concepts:wdir-pre-branch} +\end{figure} + +Una vez se ha actualizado el directorio de trabajo a un conjunto de +cambios anterior, qué pasa si se hacen cambios, y luego se hace una +consignación? Mercurial se comporta en la misma forma que describí +anteriormente. Los padres del directorio de trabajo se convierten en +los padres del nuevo conjunto de cambios. Este nuevo conjunto de +cambios no tiene hijos, así que se convierte en la nueva punta. Y el +repositorio tiene ahora dos conjuntos de cambios que no tienen hijos; +a éstos los llamamos \emph{frentes}. Usted puede apreciar la +estructura que esto crea en la figura~\ref{fig:concepts:wdir-branch}. + +\begin{figure}[ht] + \centering + \grafix{wdir-branch} + \caption{Después de una consignación hecha mientras se usaba un + conjunto de cambios anterior} + \label{fig:concepts:wdir-branch} +\end{figure} + +\begin{note} + Si usted es nuevo en Mercurial, debería tener en mente un + ``error'' común, que es usar el comando \hgcmd{pull} sin ninguna + opción. Por defecto, el comando \hgcmd{pull} \emph{no} actualiza + el directorio de trabajo, así que usted termina trayendo nuevos + conjuntos de cambios a su repositorio, pero el directorio de + trabajo sigue usando el mismo conjunto de cambios que tenía antes + de jalar. Si usted hace algunos cambios, y luego hace una + consignación, estará creando un nuevo frente, porque su directorio + de trabajo no es sincronizado a cualquiera que sea la nueva punta. + + Pongo la palabra ``error'' en comillas porque todo lo que usted + debe hacer para rectificar la situación es hacer una fusión + (\hgcmd{merge}), y luego una consignación (\hgcmd{commit}). En + otras palabras, esto casi nunca tiene consecuencias negativas; + sólo sorprende a la gente. Discutiré otras formas de evitar este + comportamiento, y porqué Mercurial se comporta de esta forma, + inicialmente sorprendente, más adelante. +\end{note} + +\subsection{Fusión de frentes} + +Cuando usted ejecuta el comando \hgcmd{merge}, Mercurial deja el +primer padre del directorio de trabajo intacto, y escribe como segundo +padre el conjunto de cambios contra el cual usted está haciendo la +fusión, como se muestra en la figura~\ref{fig:concepts:wdir-merge}. + +\begin{figure}[ht] + \centering + \grafix{wdir-merge} + \caption{Fusión de dos frentes} + \label{fig:concepts:wdir-merge} +\end{figure} + +Mercurial también debe modificar el directorio de trabajo, para +fusionar los ficheros que él monitorea en los dos conjuntos de +cambios. Con algunas simplificaciones, el proceso es el siguiente, por +cada fichero en los manifiestos de ambos conjuntos de cambios. +\begin{itemize} +\item Si ningún conjunto de cambios ha modificado un fichero, no se + hace nada con el mismo. +\item Si un conjunto de cambios ha modificado un fichero, y el otro no + lo ha hecho, se crea una copia del fichero con las modificaciones + pertinentes en el directorio de trabajo. +\item Si un conjunto de cambios borra un fichero, y el otro no lo ha + hecho (o también lo borró), se borra dicho fichero del directorio + de trabajo. +\item Si un conjunto de cambios ha borrado un fichero, pero el otro lo ha + modificado, se le pregunta al usuario qué hacer: conservar el + fichero modificado, o borrarlo? +\item Si ambos conjuntos de cambios han modificado un fichero, se + invoca el programa externo de fusión para definir el nuevo + contenido del fichero fusionado. Esto puede requerir interacción + directa de parte del usuario. +\item Si un conjunto de cambios ha modificado un fichero, y el otro ha + renombrado o copiado el mismo, asegurarse de que los cambios sigan + al nuevo nombre de fichero. +\end{itemize} +Hay más detalles---hacer una fusión tiene una gran cantidad de casos +especiales---pero éstas son las elecciones más comunes que se ven +involucradas en una fusión. Como usted puede ver, muchos de los casos +son completamente automáticos, y de hecho la mayoría de las fusiones +terminan automáticamente, sin requerir la interacción del usuario para +resolver ningún conflicto. + +Cuando considere qué pasa cuando usted hace una consignación después +de una fusión, de nuevo el directorio de trabajo es ``el conjunto de +cambios que estoy a punto de consignar''. Una vez termina su trabajo +el comando \hgcmd{merge}, el directorio de trabajo tiene dos padre; +éstos se convertirán en los padres del nuevo conjunto de cambios. + +Mercurial le permite hacer múltiples fusiones, pero usted debe +consignar los resultados de cada fusión sucesivamente. Esto es +necesario porque Mercurial sólo monitorea dos padres, tanto para las +revisiones como para los directorios de trabajo. Aunque técnicamente +es posible fusionar varios conjuntos de trabajo en una sola operación, +la posibilidad de confundir al usuario y crear un desorden terrible en +la fusión se hace incontenible de inmediato. + +\section{Otras características de diseño interesantes} + +En las secciones anteriores, he tratado de resaltar algunos de los +aspectos más importantes del diseño de Mercurial, para mostrar que se +presta gran cuidado y atención a la confiabilidad y el desempeño. Sin +embargo, la atención a los detalles no para ahí. Hay una cantidad de +aspectos de la construcción de Mercurial que encuentro interesantes +personalmente. Detallaré unos cuantos de ellos aquí, aparte de los +elementos ``importantes'' de arriba, para que, si usted está +% TODO the amount of thinking => (la cantidad de) esfuerzo mental +interesado, pueda obetener una idea mejor de la cantidad de esfuerzo +mental invertido en el diseño de un sistema bien diseñado. + + +\subsection{Compresión ingeniosa} + +Cuando es adecuado, Mercurial almacenará tanto las instantáneas como +los deltas en formato comprimido. Lo hace \emph{tratando} siempre de +comprimir una instantánea o delta, y conservando la versión comprimida +sólo si es más pequeña que la versión sin compresión. + +Esto implica que Mercurial hace ``lo correcto'' cuando almacena un +fichero cuyo formato original está comprimido, como un fichero +\texttt{zip} o una imagen JPEG. Cuando estos tipos de ficheros son +comprimidos por segunda vez, el fichero resultante usualmente es más +grande que la versión comprimida una sola vez, por lo que Mercurial +almacenará el fichero \texttt{zip} o JPEG original. + +Los deltas entre revisiones de un fichero comprimido usualmente son +más grandes que las instantáneas del mismo fichero, y Mercurial de +nuevo hace ``lo correcto'' en estos casos. Él encuentra que dicho +delta excede el umbral respecto al cual se debería almacenar una +instantánea completa del fichero, así que almacena la instantánea, +ahorrando espacio de nuevo respecto al enfoque simplista de usar +únicamente deltas. + +\subsubsection{Recompresión de red} + +Cuando almacena las revisiones en disco, Mercurial usa el algoritmo de +compresión ``deflación'' (el mismo usado en el popular formato de +fichero \texttt{zip}), que provee una buena velocidad con una tasa de +compresión respetable. Sin embargo, cuando se transmiten datos de +revisiones a través de una conexión de red, Mercurial descomprime los +datos comprimidos de las revisiones. + +Si la conexión es hecha a través de HTTP, Mercurial recomprime el +flujo completo de datos usando un algoritmo de compresión que brinda +una mejor tasa de compresión (el algoritmo Burrows-Wheeler del +ampliamente usado paquete de compresión \texttt{bzip2}). Esta +combinación de algoritmo y compresión del flujo completo de datos +(en vez de una revisión a la vez) reduce sustancialmente la cantidad +de bytes a transferir, brindando así un mejor desempeño de red sobre +casi todo tipo de redes. + +(Si la conexión se hace sobre \command{ssh}, Mercurial \emph{no} +recomprmime el flujo, porque \command{ssh} puede hacer esto por sí +mismo.) + +\subsection{Reordenado de lectura/escritura y atomicidad} + +Añadir datos al final de un fichero no es todo lo que hace falta para +garantizar que un lector no verá una escritura parcial. Si recuerda la +figura~\ref{fig:concepts:metadata}, las revisiones en la bitácora de +cambios apuntan a revisiones en el manifiesto, y las revisiones en el +manifiesto apuntan a revisiones en ficheros de registro. Esta +jerarquía es deliberada. + +Un escritor inicia una transacción al escribir los datos del ficheros +del fichero de registro y el manifiesto, y no escribe nada en la +bitácora de cambios hasta que dichas escrituras hayan terminado. Un +lector empieza leyendo datos de la bitácora de cambios, luego del +manifiesto, y finalmente del fichero de registro. + +%TODO revisar párrafo completo, no me gusta el resultado +Como el escritor siempre termina de escribir los datos en el fichero +de registro y en el manifiesto antes de escribir a la bitácora de +cambios, un lector nunca verá un apuntador a una versión parcialmente +escrita de revisiones del manifiesto desde la bitácora de cambios, y +nunca leerá un apuntador a una revisión parcialmente escrita del +fichero de registro desde el manifiesto. + +\subsection{Acceso concurrente} + +El reordenado de lectura/escritura y la atomicidad garantizan que +Mercurial nunca necesita \emph{bloquear} un repositorio cuando está +leyendo datos, aún si se está escribiendo al repositorio mientras se +hace la lectura. Esto tiene un gran efecto en la escalabilidad; usted +puede tener cualquier cantidad de procesos Mercurial leyendo datos de +un repositorio de manera segura al mismo tiempo, sin importar si se +está escribiendo al mismo o no. + +La naturaleza carente de bloqueos de la lectura significa que si usted +está compartiendo un repositorio en un sistema multiusuario, no +necesita dar a los usuarios locales permisos de \emph{escritura} a su +repositorio para que ellos puedan clonarlo o jalar cambios; sólo +necesitan permisos de \emph{lectura}. (Esta \emph{no} es una +%TODO signo de admiración de apertura +característica común entre los sistemas de control de revisiones, así +que no la dé por hecha! Muchos de ellos requieren que los lectores +sean capaces de bloquear el repositorio antes de poder leerlo, y esto +requiere acceso de escritura en al menos un directorio, lo que por +supuesto se convierte en una fuente de todo tipo de problemas +administrativos y de seguridad bastante molestos.) + +Mercurial usar bloqueos para asegurarse de que sólo un proceso pueda +escribir a un repositorio al mismo tiempo (el mecanismo de bloqueo es +seguro incluso sobre sistemas de ficheros notoriamente hostiles al +bloqueo, como NFS). Si un repositorio está bloqueado, los escritores +esperarán un buen rato para revisar si el repositorio ya ha sido +desbloqueado, pero si el repositorio sique bloqueado por mucho tiempo, +el proceso que intenta escribir fallará por tiempo de espera máximo. +Esto significa que sus guiones automáticos diarios no se quedarán +esperando para siempre, apilándose si el sistema se cayó sin que nadie +se diera cuenta, por ejemplo. (Sí, el tiempo de espera máximo es +configurable, de cero a infinito). + + +\subsubsection{Acceso seguro al estado de directorio} + +Al igual que con los datos de revisión, Mercurial no requiere un +bloqueo para leer el fichero de estado de directorio; sí se usa un +bloqueo para escribir a él. Para evitar la posibilidad de leer una +copia parcialmente escrita del fichero de estado de directorio, +Mercurial escribe a un fichero con un nombre único en el mismo +directorio del fichero de estado de directorio, y luego renombra +atómicamente este fichero temporal a \filename{dirstate}\ndt{Estado de +directorio.}. Así se garantiza que el fichero llamado +\filename{dirstate} esté completo, y no parcialmente escrito. + +\subsection{Evitar movimientos de brazo} + +Un aspecto crítico para el desempeño de Mercurial es evitar los +movimientos del brazo de lectura del disco duro, ya que cualquier +movimiento de brazo es mucho más costoso que incluso una operación de +lectura relativamente grande. + +Es por esto que, por ejemplo, el estado de directorio es almacenado +como un solo fichero. Si hubiera un estado de directorio por cada +directorio que Mercurial monitorea, el disco haría un movimiento de +brazo por cada directorio. En cambio, Mercurial lee el estado de +directorio completo en un solo paso. + +Mercurial también usa un esquema de ``copiar al escribir'' cuando +clona un repositorio en un mismo medio de almacenamiento local. En vez +de copiar cada fichero de revlog del repositorio viejo al nuevo, se +crea un ``enlace duro'', que es una manera sucinta de decir +``estos dos nombres apuntan al mismo fichero''. Cuando Mercurial está +a punto de escribir a uno de los ficheros de revlog, revisa si la +cantidad de nombres apuntando al fichero es de más de uno. Si lo es, +más de un repositorio está usando el fichero, así que Mercurial hace +una nueva copia del fichero, privada para este repositorio. + +Algunos desarrolladores de control de revisiones han indicado que la +idea de hacer una copia privada completa de un fichero no es eficiente +desde el punto de vista de almacenamiento. Aunque esto es cierto, el +almacenamiento es barato, y este método brinda el máximo rendimiento +al tiempo que delega la mayor parte del trabajo de manejo de ficheros +al sistema operativo. Un esquema alternativo seguramente reduciría el +desempeño y aumentaría la complejidad del software, cada uno de los +cuales es mucho más importante para la ``sensación'' que se tiene del +software en el trabajo día a día. + +\subsection{Otros contenidos del estado de directorio} + +Debido a que Mercurial no lo fuerza a indicar si usted está +modificando un fichero, se usa el estado de directorio para almacenar +información extra para poder determinar efecientemente si usted ha +modificado un fichero. Por cada fichero en el directorio de trabajo, +se almacena el momento en que Mercurial modificó por última vez el +fichero, y el tamaño del fichero en ese momento. + +Cuando usted añade (\hgcmd{add}), remueve (\hgcmd{remove}), renombra +(\hgcmd{rename}) o copia (\hgcmd{copy}) ficheros, Mercurial actualiza +el estado de directorio para saber qué hacer con dichos ficheros +cuando usted haga la consignación. + +Cuando Mercurial está revisando el estado de los ficheros en el +directorio de trabajo, revisa primero la fecha de modificación del +fichero. Si no ha cambiado, el fichero no ha sido modificado. Si el +tamaño del fichero ha cambiado, el fichero ha sido modificado. Sólo en +el caso en que el tiempo de modificación ha cambiado, pero el tamaño +no, es necesario leer el contenido del fichero para revisar si ha +cambiado. Almacenar estos pocos datos reduce dramáticamente la +cantidad de datos que Mercurial debe leer, lo que brinda una mejora en +el rendimiento grande, comparado con otros sistemas de control de +revisiones. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/daily.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,404 @@ +\chapter{Mercurial día a día} +\label{chap:daily} + +\section{Cómo indicarle a Mercurial qué ficheros seguir} + +Mercurial no trabaja con ficheros en su repositorio a menos que usted +se lo indique explícitamente. La orden \hgcmd{status} le mostrará +cuáles ficheros son desconocidos para Mercurial; se emplea un +``\texttt{?}'' para mostrar tales ficheros. + +Para indicarle a Mercurial que tenga en cuenta un fichero, emplee la +orden \hgcmd{add}. Una vez que haya adicionado el fichero, la línea +referente al fichero al aplicar la orden \hgcmd{status} para tal +fichero cambia de ``\texttt{?}'' a ``\texttt{A}''. +\interaction{daily.files.add} + +Después de invocar \hgcmd{commit}, los ficheros que haya adicionado +antes de consignar no se listarán en la salida de \hgcmd{status}. La +razón para esto es que \hgcmd{status} solamente le muestra aquellos +ficheros ``interesantes'' ---~los que usted haya modificado o a aquellos +sobre los que usted haya indicado a Mercurial hacer algo--- de forma +predeterminada. Si tiene un repositorio que contiene miles de +ficheros, rara vez deseará saber cuáles de ellos están siendo +seguidos por Mercurial, pero que no han cambiado. (De todas maneras, +puede obtener tal información; más adelante hablaremos de ello.) + + +Cuando usted añade un fichero, Mercurial no hace nada con él inmediatamente. +En cambio, tomará una instantánea del estado del fichero la próxima vez +que usted consigne. Continuará haciendo seguimiento a los cambios que +haga sobre el fichero cada vez que consigne, hasta que usted lo elimine. + +\subsection{Nombramiento explícito e implícito de ficheros} + +Mercurial tiene un comportamiento útil en el cual si a una orden, +le pasa el nombre de un directorio, todas las órdenes lo interpretarán como +``Deseo operar en cada fichero de este directorio y sus +subdirectorios''. +\interaction{daily.files.add-dir} +Tenga en cuenta que en este ejemplo Mercurial imprimió los nombres de +los ficheros que se adicionaron, mientras que no lo hizo en el ejemplo +anterior cuando adicionamos el fichero con nombre \filename{a}. + +En el último caso hicimos explícito el nombre del fichero que +deseábamos adicionar en la línea de órdenes, y Mercurial asume en +tales casos que usted sabe lo que está haciendo y no imprime +información alguna. + +Cuando hacemos \emph{implícitos} los nombres de los ficheros dando el +nombre de un directorio, Mercurial efectúa el paso extra de imprimir +el nombre de cada fichero con el que va a hacer algo. Esto para +aclarar lo que está sucediendo, y reducir en lo posible una sorpresa +silenciosa pero fatal. Este comportamiento es común a la mayoría de +órdenes en Mercurial. + +\subsection{Nota al margen: Mercurial trata ficheros, no directorios} + +Mercurial no da seguimiento a la información de los directorios. En +lugar de eso tiene en cuenta las rutas de los ficheros. Antes de +crear un fichero, primero crea todos los directorios que hagan falta +para completar la ruta del mismo. Después de borrar un fichero, +borra todos los directorios vacíos que estuvieran en la ruta del +fichero borrado. Suena como una diferencia trivial, pero tiene una +consecuencia práctica menor: no es posible representar un directorio +completamente vacío en Mercurial. + +Los directorios vacíos rara vez son útiles, y hay soluciones +alternativas no intrusivas que usted puede emplear para obtener el efecto +apropiado. Los desarrolladores de Mercurial pensaron que la +complejidad necesaria para administrar directorios vacíos no valía la +pena frente al beneficio limitado que esta característica podría traer. + +Si necesita un directorio vacío en su repositorio, hay algunas formas +de lograrlo. Una es crear un directorio, después hacer \hgcmd{add} a +un fichero ``oculto'' dentro de ese directorio. En sistemas tipo +Unix, cualquier fichero cuyo nombre comience con un punto +(``\texttt{.}'') es tratado como oculto por la mayoría de +comandos y herramientas GUI. Esta aproximación se ilustra en la figura~\ref{ex:daily:hidden}. + +\begin{figure}[ht] + \interaction{daily.files.hidden} + \caption{Simular un directorio vacío con un fichero oculto} + \label{ex:daily:hidden} +\end{figure} + +Otra forma de abordar la necesidad de un directorio vacío es +simplemente crear uno en sus guiones de construcción antes de que lo +necesiten. + +\section{Cómo dejar de hacer seguimiento a un fichero} + +Si decide que un fichero no pertenece a su repositorio, use la orden +\hgcmd{remove}; se borrará el fichero y le indicará a Mercurial que +deje de hacerle seguimiento. Los ficheros eliminados se representan +con ``\texttt{R}'' al usar \hgcmd{status}. +\interaction{daily.files.remove} + +Después de hacer \hgcmd{remove} a un fichero, Mercurial dejará de +hacer seguimiento al mismo, incluso si recrea el fichero con el mismo +nombre en su directorio de trabajo. Si decide recrear un fichero con +el mismo nombre y desea que Mercurial le haga seguimiento, basta con +hacerle \hgcmd{add}. Mercurial sabrá que el fichero recientemente +adicionado no está relacionado con el fichero anterior que tenía el +mismo nombre. + +\subsection{Al eliminar un fichero no se afecta su historial} + +Es preciso tener en cuenta que eliminar un fichero tiene sólo dos +efectos. +\begin{itemize} +\item Se elimina la versión actual del fichero del directorio de +trabajo. +\item Mercurial deja de hacer seguimiento a los cambios del fichero + desde la próxima consignación. +\end{itemize} +Al eliminar un fichero \emph{no} se altera de ninguna manera el +\emph{historial} del mismo. + +Si actualiza su directorio de trabajo a un conjunto de cambios en el +cual el fichero que eliminó aún era tenido en cuenta, éste reaparecerá en +el directorio de trabajo, con los contenidos que este tenía cuando se +consignó tal conjunto de cambios. Si usted actualiza el directorio de +trabajo a un conjunto de cambios posterior en el cual el fichero había +sido eliminado, Mercurial lo eliminará de nuevo del directorio de +trabajo. + +\subsection{Ficheros perdidos} + +Mercurial considera como \emph{perdido} un fichero que usted borró, +pero para el que no se usó \hgcmd{remove}. Los ficheros perdidos se +representan con ``\texttt{!}'' al visualizar \hgcmd{status}. +Las órdenes de Mercurial generalmente no harán nada con los ficheros +perdidos. +\interaction{daily.files.missing} + +Si su repositorio contiene un fichero que \hgcmd{status} reporta como +perdido, y desea que el mismo se vaya, se puede usar +\hgcmdargs{remove}{\hgopt{remove}{--after}} posteriormente para +indicarle a Mercurial que usted deseaba borrar tal fichero. +\interaction{daily.files.remove-after} + +Por otro lado, si borró un fichero perdido por accidente, puede usar +\hgcmdargs{revert}{\emph{nombre de fichero}} para recuperar el +fichero. Reaparecerá, sin modificaciones. +\interaction{daily.files.recover-missing} + +\subsection{Nota al margen: ¿Por qué decirle explícitamente a Mercurial + que elimine un fichero?} + +Es posible que se haya preguntado por qué Mercurial exige que usted le +indique explícitamente que está borrando un fichero. Al principio del +desarrollo de Mercurial, este permitía que usted borrara el fichero +sin más; Mercurial se daría cuenta de la ausencia del fichero +automáticamente después de la ejecución de \hgcmd{commit}, y dejaría de +hacer seguimiento al fichero. En la práctica, resultaba muy sencillo +borrar un fichero accidentalmente sin darse cuenta. + +\subsection{Atajo útil---agregar y eliminar ficheros en un solo paso} + +Mercurial ofrece una orden combinada, \hgcmd{addremove}, que agrega +los ficheros que no tienen seguimiento y marca los ficheros faltantes +como eliminados. +\interaction{daily.files.addremove} +La orden \hgcmd{commit} se puede usar con la opción \hgopt{commit}{-A} +que aplica el mismo agregar-eliminar, seguido inmediatamente de una +consignación. +\interaction{daily.files.commit-addremove} + +\section{Copiar ficheros} + +Mercurial ofrece la orden \hgcmd{copy} para hacer una copia nueva de +un fichero. Cuando se copia un fichero con esta orden, Mercurial +lleva un registro indicando que el nuevo fichero es una copia del +fichero original. Los ficheros copiados se tratan de forma especial cuando +usted hace una fusión con el trabajo de alguien más. + +\subsection{Resultados de copiar un fichero durante una fusión} + +Durante una fusión los cambios ``siguen'' una copia. Para ilustrar +lo que esto significa, haremos un ejemplo. Comenzaremos con el mini +repositorio usual que contiene un solo fichero +\interaction{daily.copy.init} +Debemos hacer algo de trabajo en paralelo, de forma que tengamos algo para +fusionar. Aquí clonamos el repositorio. +\interaction{daily.copy.clone} +De vuelta en el repositorio inicial, usemos la orden \hgcmd{copy} para hacer +una copia del primer fichero que creamos. +\interaction{daily.copy.copy} + +Si vemos la salida de la orden \hgcmd{status}, el fichero copiado luce +tal como un fichero que se ha añadido normalmente. +\interaction{daily.copy.status} +Pero si usamos la opción \hgopt{status}{-C} de la orden +\hgcmd{status}, se imprimirá otra línea: el fichero \emph{desde} el +cual fue copiado nuestro fichero recién añadido. +\interaction{daily.copy.status-copy} + +Ahora, en el repositorio que clonamos, hagamos un cambio en +paralelo. Adicionaremos una línea de contenido al fichero original que +creamos. +\interaction{daily.copy.other} +Hemos modificado el fichero \filename{file} en este +repositorio. Cuando jalemos los cambios del primer repositorio y +fusionemos las dos cabezas, Mercurial propagará los cambios que hemos +hecho localmente en \filename{file} a su copia, \filename{new-file}. +\interaction{daily.copy.merge} + +\subsection{¿Por qué los cambios se reflejan en las copias?} +\label{sec:daily:why-copy} + +Este comportamiento de cambios en ficheros que se propagan a las +copias de los ficheros parecería esotérico, pero en la mayoría de +casos es absolutamente deseable. +Es indispensable recordar que esta propagación \emph{solamente} sucede +cuando fusionamos. Por lo tanto si sobre un fichero se usa +\hgcmd{copy}, y se modifica el fichero original durante el curso +normal de su trabajo, nada pasará. + +Lo segundo a tener en cuenta es que las modificaciones solamente se +propagarán en las copias únicamente si los repositorios de los cuales +está jalando los cambios \emph{no saben} de la copia. + +Explicaremos a continuación la razón de este comportamiento de +Mercurial. Digamos que yo he aplicado un arreglo de un fallo importante a un +fichero fuente y consigné los cambios. Por otro lado, usted decidió hacer +\hgcmd{copy} sobre el fichero en su repositorio, sin saber acerca del +fallo o sin ver el arreglo, y ha comenzado a trabajar sobre su copia +del fichero. + +Si jala y fusiona mis cambios y Mercurial \emph{no hubiera} propagado +los cambios en las copias, su fichero fuente tendría el fallo, a menos +que usted haya recordado propagar el arreglo del fallo a mano, el +mismo \emph{permanecería} en su copia del fichero. + +Mercurial previene esta clase de problemas, gracias a la propagación +automática del cambio que arregló el fallo del fichero original. Hasta +donde sé, Mercurial es el \emph{único} sistema de control de +revisiones que propaga los cambios en las copias de esta forma. + +Cuando su historial de cambios tiene un registro de la copia y la +subsecuente fusión, usualmente no es necesario propagar los cambios el +fichero original a las copias del mismo, y por esta razón Mercurial +propaga únicamente los cambios en las copias hasta este punto y no más +allá. + + +\subsection{Cómo hacer que los cambios \emph{no} sigan a la copia?} + +Si por algún motivo usted decide que esta característica de +propagación automática de cambios en las copias no es para usted, +simplemente use +la orden usual de su sistema para copiar ficheros (en sistemas tipo +Unix, es \command{cp}), y posteriormente use \hgcmd{add} sobre la nueva +copia hecha a mano. Antes de hacerlo, de todas maneras, relea la +sección~\ref{sec:daily:why-copy}, y tome una decisión asegurándose que +este comportamiento no es el apropiado para su caso específico. + +\subsection{Comportamiento de la orden \hgcmd{copy}} + +Cuando usa la orden \hgcmd{copy}, Mercurial hace una copia de cada +fichero fuente tal como se encuentra en el directorio actual. Esto +significa que si usted hace +modificaciones a un fichero, y le aplica \hgcmd{copy} sin haber +consignado primero los cambios, la nueva copia contendrá también las +modificaciones que haya hecho hasta ese punto. (Este comportamiento me +parece poco intuitivo, y por tal motivo lo menciono.) + +La orden \hgcmd{copy} actúa de forma parecida a la orden \command{cp} +de Unix (puede usar el alias \hgcmd{cp} si le es más cómodo). El +último argumento es el \emph{destino}, y todos los argumentos previos +son las \emph{fuentes}. Si solamente indica un fichero como la +fuente, y el destino no existe, se crea un fichero nuevo con ese nombre. +\interaction{daily.copy.simple} +Si el destino es un directorio, Mercurial copia las fuentes en éste. +\interaction{daily.copy.dir-dest} +La copia de un directorio es recursiva, y preserva la estructura del +directorio fuente. +\interaction{daily.copy.dir-src} +Si tanto la fuente como el destino son directorios, la estructura de +la fuente se recrea en el directorio destino. +\interaction{daily.copy.dir-src-dest} + +De la misma forma que la orden \hgcmd{rename}, si copia un fichero +manualmente y desea que Mercurial sepa que ha copiado un fichero, +basta con aplicar la opción \hgopt{copy}{--after} a la orden +\hgcmd{copy}. +\interaction{daily.copy.after} + +\section{Renombrar ficheros} + +La necesidad de renombrar un fichero es más común que hacer una copia +del mismo. La razón por la cual discutí la orden \hgcmd{copy} antes +de hablar acerca de cambiar el nombre de los ficheros, es que +Mercurial trata el renombrar un fichero de la misma forma que una +copia. Por lo tanto, saber lo que hace Mercurial cuando usted copia +un fichero le indica qué esperar cuando renombra un fichero. + +Cuando usa la orden \hgcmd{rename}, Mercurial hace una copia de cada +fichero fuente, lo borra y lo marca como fichero eliminado. +\interaction{daily.rename.rename} +La orden \hgcmd{status} muestra la nueva copia del fichero como +añadida y el fichero inicial de la copia, como eliminado. +\interaction{daily.rename.status} +De la misma forma en que se usa la orden \hgcmd{copy}, debemos usar la +opción \hgopt{status}{-C} de la orden \hgcmd{status} para verificar +que el fichero añadido realmente comienza a ser seguido por Mercurial +como una copia del fichero original, ahora eliminado. +\interaction{daily.rename.status-copy} + +Igual que con \hgcmd{remove} y \hgcmd{copy}, puede indicársele a +Mercurial acerca de un renombramiento inmediato con la opción +\hgopt{rename}{--after}. El comportamiento de la orden +\hgcmd{rename} y las opciones que acepta, son similares a la orden +\hgcmd{copy} en casi todo. + +\subsection{Renombrar ficheros y fusionar cambios} + +Dado que el renombrado de Mercurial se implementa como un +copiar-y-eliminar, la misma propagación de cambios ocurre cuando usted +fusiona después de renombrar como después de hacer una copia. + +Si yo modifico un fichero y usted lo renombra a un nuevo fichero, y +posteriormente fusionamos nuestros respectivos cambios, mi +modificación al fichero bajo su nombre original se propagará en el +fichero con el nuevo nombre. (Es lo que se esperaría que ``simplemente +funcione,'' +pero, no todos los sistemas de control de revisiones hacen esto.) + +Aunque el hecho de que los cambios sigan la copia es una característica +respecto a la cual usted puede estar de acuerdo y decir ``si, puede +ser útil,'' debería ser claro +que el seguimiento de cambios de un renombramiento es definitivamente +importante. Sin esto, sería muy sencillo que los cambios se +quedaran atrás cuando los ficheros se renombran. + +\subsection{Cambios de nombre divergentes y fusión} + +El caso de renombramiento con nombres divergentes ocurre cuando dos +desarrolladores comienzan con un fichero---llamémoslo +\filename{foo}---en sus repositorios respectivos. + +\interaction{rename.divergent.clone} +%TODO we must either change the example's names, and these names, or +%use the original names. as of now, we keep the old names +Anne renombra el fichero a \filename{bar}. +\interaction{rename.divergent.rename.anne} +Mientras que Bob lo renombra como \filename{quux}. +\interaction{rename.divergent.rename.bob} + +Veo esto como un conflicto porque cada desarrollador ha expresado +intenciones diferentes acerca de cómo considera debería haberse +nombrado el fichero. + +¿Qué cree que debería pasar cuando fusionen su trabajo? +El comportamiento de Mercurial es que siempre preserva \emph{ambos} +nombres cuando fusiona los conjuntos de cambios que contienen nombres +divergentes. +%TODO traducir texto interaccion +\interaction{rename.divergent.merge} + +Tenga en cuenta que Mercurial le advierte acerca de nombres +divergentes, pero deja que usted decida qué hacer con la divergencia +después de la fusión. + +\subsection{Cambios de nombre convergentes y fusión} + +Otra clase de conflicto al cambiar el nombre de ficheros ocurre cuando dos +personas eligen renombrar diferentes ficheros \emph{fuente} al mismo +\emph{destino}. En este caso Mercurial aplica su maquinaria de fusión +usual, y le permite a usted guiar la situación a una resolución adecuada. + +\subsection{Otros casos límite relacionados con renombramientos} + +Mercurial tiene un fallo de mucho tiempo en el cual no es capaz de +fusionar cuando por un lado hay un fichero con un nombre dado, +mientras que en otro hay un directorio con el mismo nombre. Esto está +documentado como~\bug{29}. +\interaction{issue29.go} + +\section{Recuperarse de equivocaciones} + +Mercurial tiene unas órdenes poderosas que le ayudarán a recuperarse +de equivocaciones comunes. + +La orden \hgcmd{revert} le permite deshacer cambios que haya hecho a +su directorio de trabajo. Por ejemplo, si aplicó \hgcmd{add} a un +fichero por accidente, ejecute \hgcmd{revert} con el nombre del +fichero que añadió, y en tanto que el fichero no haya sido tocado de +forma alguna, no será adicionado, ni seguido por Mercurial. También +puede usar \hgcmd{revert} para deshacerse de cambios erróneos a un +fichero. + +Tenga en cuenta que la orden \hgcmd{revert} se usa para cambios que no +han sido consignados. Cuando haya consignado un cambio, si decide que +era un error, puede hacer algo todavía, pero sus opciones pueden estar +más limitadas. + +Para obtener información acerca de la orden \hgcmd{revert} y detalles +de cómo tratar con cambios consignados, vea el capítulo~\ref{chap:undo}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/backout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/backout Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/bisect --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/bisect Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,96 @@ +#!/bin/bash + +if hg -v | head -1 | grep -e "version 0.*" +then +#On mercurial 1.0 and later bisect is a builtin +echo '[extensions]' >> $HGRC +echo 'hbisect =' >> $HGRC +fi + +# 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 + +if hg -v | head -1 | grep -e "version 0.*" +then +#On mercurial 1.0 --init disappeared +hg bisect --init +fi + +#$ 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 5981a0f7540a -r 019040fbf5f5 es/examples/branch-named --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-named Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,73 @@ +#!/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 + +#$ name: merge + +hg branch +hg merge foo +hg commit -m 'Merge' +hg tip diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/branch-repo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-repo Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/branching --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branching Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/cmdref --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/cmdref Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/daily.copy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.copy Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.files Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/daily.rename --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.rename Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/daily.revert --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.revert Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/data/check_whitespace.py Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/data/netplug-1.2.5.tar.bz2 Binary file es/examples/data/netplug-1.2.5.tar.bz2 has changed diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/data/netplug-1.2.8.tar.bz2 Binary file es/examples/data/netplug-1.2.8.tar.bz2 has changed diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/data/remove-redundant-null-checks.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/data/remove-redundant-null-checks.patch Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/extdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/extdiff Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/filenames --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/filenames Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/hook.msglen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.msglen Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/hook.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.simple Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.ws Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/issue29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/issue29 Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.dodiff Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.guards --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.guards Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.id --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.id Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.qinit-help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.qinit-help Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,7 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: help +hg help qinit diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/mq.tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tarball Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.tools --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tools Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/mq.tutorial --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tutorial Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/rename.divergent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rename.divergent Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/rollback --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rollback Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/run-example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/run-example Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/svn-long.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-long.txt Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/svn-short.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-short.txt Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/svn.style --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.style Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,2 @@ +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/svn.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.template Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,5 @@ +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/tag --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tag Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/template.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.simple Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/template.svnstyle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.svnstyle Tue Apr 21 00:36:40 2009 +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 5981a0f7540a -r 019040fbf5f5 es/examples/tour --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,194 @@ +#!/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 0272e0d5a517 +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: merge.dummy1 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-hello + +#$ 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.dummy2 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-new-hello + +#$ name: merge.cat + +cat hello.c +cat ../my-hello/hello.c + +#$ name: merge.pull + +hg pull ../my-hello + +#$ name: merge.dummy3 + +hg log -r 6 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV6.my-new-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.dummy4 + +hg log -r 7 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV7.my-new-hello + +#$ name: merge.tip + +hg tip diff -r 5981a0f7540a -r 019040fbf5f5 es/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour-merge-conflict Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,73 @@ +#!/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 < cripto; + maestro -> sistemadearchivos; + maestro -> ipc; + maestro -> memoria; + maestro -> red; + maestro -> seguridad; +} diff -r 5981a0f7540a -r 019040fbf5f5 es/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filelog.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + Directorio de trabajo + Repositorio + + diff -r 5981a0f7540a -r 019040fbf5f5 es/filenames.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filenames.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,344 @@ +\chapter{Nombres de ficheros y asociación de patrones} +\label{chap:names} + +Mercurial provee mecanismos que le permiten trabajar con nombres de +ficheros en una manera consistente y expresiva. + +\section{Nombrado de ficheros simple} + +% TODO traducción literal de "under the hood". revisar +Mercurial usa un mecanismo unificado ``bajo el capó'' para manejar +nombres de ficheros. Cada comando se comporta de manera uniforme con +respecto a los nombres de fichero. La manera en que los comandos +operan con nombres de fichero es la siguiente. + +Si usted especifica explícitamente nombres reales de ficheros en la +línea de comandos, Mercurial opera únicamente sobre dichos ficheros, +como usted esperaría. +\interaction{filenames.files} + +Cuando usted provee el nombre de un directorio, Mercurial interpreta +eso como ``opere en cada fichero en este directorio y sus +subdirectorios''. Mercurial va por todos los ficheros y subdirectorios +de un directorio en orden alfabético. Cuando encuentra un +subdirectorio, lo recorrerá antes de continuar con el directorio +actual. +\interaction{filenames.dirs} + +\section{Ejecución de comandos sin ningún nombre de fichero} + +Los comandos de Mercurial que trabajan con nombres de fichero tienen +comportamientos por defecto adecuados cuando son utilizados sin pasar +ningún patrón o nombre de fichero. El tipo de comportamiento depende +de lo que haga el comando. Aquí presento unas cuantas reglas generales +que usted puede usar para que es lo que probablemente hará un comando +si usted no le pasa ningún nombre de fichero con el cual trabajar. +\begin{itemize} +\item Muchos comandos operarán sobre el directorio de trabajo + completo. Por ejemplo, esto es lo que hace el comando + \hgcmd{add}, +\item Si el comando tiene efectos difíciles o incluso imposibles de + revertir, se le obligará a usted a proveer explícitamente al menos + % TODO revisar ese "lo proteje a usted" + un nombre o patrón (ver más abajo). Esto lo proteje a usted de, + por ejemplo, borrar ficheros accidentalmente al ejecutar + \hgcmd{remove} sin ningún argumento. +\end{itemize} + + +Es fácil evitar este comportamiento por defecto, si no es el adecuado +para usted. Si un comando opera normalmente en todo el directorio de +trabajo, usted puede llamarlo para que trabaje sólo en el directorio +actual y sus subdirectorio pasándole el nombre ``\dirname{.}''. +\interaction{filenames.wdir-subdir} + +Siguiendo la misma línea, algunos comandos normalmente imprimen las +rutas de ficheros con respecto a la raíz del repositorio, aún si usted +los llama dentro de un subdirectorio. Dichos comandos imprimirán las +rutas de los ficheros respecto al directorio en que usted se encuentra +si se les pasan nombres explícitos. Vamos a ejecutar el comando +\hgcmd{status} desde un subdirectorio, y a hacer que opere en el +directorio de trabajo completo, a la vez que todas las rutas de +ficheros se imprimen respecto a nuestro subdirectorio, pasándole la +salida del comando \hgcmd{root}. +\interaction{filenames.wdir-relname} + +\section{Reportar que está pasando} + +El ejemplo con el comando \hgcmd{add} en la sección anterior ilustra +algo más que es útil acerca de los comandos de Mercurial. Si un +comando opera en un fichero que usted no pasó explícitamente en la +línea de comandos, usualmente se imprimirá el nombre del fichero, para +que usted no sea sorprendido por lo que sucede. + +Esto es el principio de \emph{mínima sorpresa}. Si usted se ha +referido explícitamente a un fichero en la línea de comandos, no tiene +mucho sentido repetir esto de vuelta a usted. Si Mercurial está +actuando en un fichero \emph{implícitamente}, porque usted no pasó +nombres, ni directorios, ni patrones (ver más abajo), lo más seguro es +decirle a usted qué se está haciendo. + +Usted puede silenciar a los comandos que se comportan de esta manera +usando la opción \hggopt{-q}. También puede hacer que impriman el +nombre de cada fichero, aún aquellos que usted indicó explícitamente, +usando la opción \hggopt{-v}. + +\section{Uso de patrones para identificar ficheros} + +Además de trabajar con nombres de ficheros y directorios, Mercurial le +permite usar \emph{patrones} para identificar ficheros. El manejo de +patrones de Mercurial es expresivo. + +En sistemas tipo Unix (Linux, MacOS, etc.), el trabajo de asociar +patrones con nombres de ficheros recae sobre el intérprete de comandos. +En estos sistemas, usted debe indicarle explícitamente a Mercurial que +el nombre que se le pasa es un patrón. En Windows, el intérprete no +expande los patrones, así que Mercurial identificará automáticamente +los nombres que son patrones, y hará la expansión necesaria. + +Para pasar un patrón en vez de un nombre normal en la línea de +comandos, el mecanismo es simple: +\begin{codesample2} + syntax:patternbody +\end{codesample2} +Un patrón es identificado por una cadena de texto corta que indica qué +tipo de patrón es, seguido por un dos puntos, seguido por el patrón en +sí. + +Mercurial soporta dos tipos de sintaxis para patrones. La que se usa +con más frecuencia se denomina \texttt{glob}\ndt{Grupo, colección, +aglomeración.}; es el mismo tipo de asociación de patrones usado por +el intérprete de Unix, y también debería ser familiar para los +usuarios de la línea de comandos de Windows. + +Cuando Mercurial hace asociación automática de patrones en Windows, +usa la sintaxis \texttt{glob}. Por esto, usted puede omitir el +prefijo ``\texttt{glob:}'' en Windows, pero también es seguro usarlo. + +La sintaxis \texttt{re}\ndt{Expresiones regulares.} es más poderosa; +le permite especificar patrones usando expresiones regulares, también +conocidas como regexps. + +A propósito, en los ejemplos siguientes, por favor note que yo tengo +el cuidado de rodear todos mis patrones con comillas sencillas, para +que no sean expandidos por el intérprete antes de que Mercurial pueda +verlos. + +\subsection{Patrones \texttt{glob} estilo intérprete} + +Este es un vistazo general de los tipos de patrones que usted puede +usar cuando está usando asociación con patrone glob. + +La secuencia ``\texttt{*}'' se asocia con cualquier cadena, dentro de +un único directorio. +\interaction{filenames.glob.star} + +La secuencia ``\texttt{**}'' se asocia con cualquier cadena, y cruza los +% TODO token +límites de los directorios. No es una elemento estándar de los tokens +de glob de Unix, pero es aceptado por varios intérpretes Unix +populares, y es muy útil. +\interaction{filenames.glob.starstar} + +La secuencia ``\texttt{?}'' se asocia con cualquier caracter sencillo. +\interaction{filenames.glob.question} + +El caracter ``\texttt{[}'' marca el inicio de una \emph{clase de +caracteres}. Ella se asocia con cualquier caracter sencillo dentro de +la clase. La clase se finaliza con un caracter ``\texttt{]}''. Una +clase puede contener múltiples \emph{rango}s de la forma +``\texttt{a-f}'', que en este caso es una abreviación para +``\texttt{abcdef}''. +\interaction{filenames.glob.range} +Si el primer caracter en aparecer después de ``\texttt{[}'' en la +clase de caracteres es un ``\texttt{!}'', se \emph{niega} la clase, +haciendo que se asocie con cualquier caracter sencillo que no se +encuentre en la clase. + +Un ``\texttt{\{}'' marca el inicio de un grupo de subpatrones, en +donde todo el grupo es asociado si cualquier subpatrón en el grupo +puede ser asociado. El caracter ``\texttt{,}'' separa los subpatrones, +y el ``\texttt{\}}'' finaliza el grupo. +\interaction{filenames.glob.group} + +\subsubsection{Cuidado!} + +No olvide que si usted desea asocia un patrón con cualquier +directorio, no debería usar el elemento para asociar con cualquier +cadena ``\texttt{*}'', ya que éste sólo generará asociaciones dentro +de un solo directorio. En vez de eso, use el caracter para asociar con +cualquier cadena ``\texttt{**}''. Este pequeño ejemplo ilustra la +diferencia entre los dos. +\interaction{filenames.glob.star-starstar} + +\subsection{Asociación con patrones de expresiones regulares \texttt{re}} + +Mercurial acepta la misma sintaxis para expresiones regulares del +lenguaje de programación Python (internamente se usa el motor de +expresiones regulares de Python). Esta sintaxis está basada en la +misma del lenguaje Perl, que es el dialecto más popular en uso +(por ejemplo, también se usa en Java). + +No discutiré el dialecto de expresiones regulares de Mercurial en +detalle aquí, ya que las mismas no son usadas frecuentemente. Las +expresiones regulares al estilo Perl se encuentran documentadas +exhaustivamente en una multitud de sitios web, y en muchos libros. +En vez de eso, me enfocaré en unas cuantas cosas que usted debería +conocer si tiene la necesidad de usar expresiones regulares en +Mercurial. + +Una expresión regular es comparada contra un nombre de fichero +completo, relativo a la raíz del repositorio. En otras palabras, aún +si usted se encuentra en un subdirectorio \dirname{foo}, si desea +asociar ficheros en este directorio, su patrón debe empezar con +``\texttt{foo/}''. + +Un detalle a tener en cuenta es que, si le son familiares las +expresiones regulares al estilo Perl, las de Mercurial están +\emph{enraízadas}. Esto es, que la asociación de una expresión se hace +desde el inicio de la cadena; no se buscan coincidencias dentro de la +cadena. Para buscar coincidencias en cualquier sitio dentro de una +cadena, empiece su patrón con un ``\texttt{.*}''. + +\section{Filtrado de ficheros} + +Mercurial no sólo le provee una variedad de formas para especificar +ficheros; le permite limitar aún más dichos ficheros mediante el uso +de \emph{filtros}. Los comandos que operan con nombres de fichero +aceptan dos opciones de filtrado. +\begin{itemize} +\item \hggopt{-I}, o \hggopt{--include}, le permite especificar un + patrón con el que deben coincidir los ficheros para ser + procesados. +\item \hggopt{-X}, o \hggopt{--exclude}, le brinda una manera de + \emph{evitar} procesar ficheros, si coinciden con este patrón. +\end{itemize} +Usted puede pasar múltiples veces las opciones \hggopt{-I} y +\hggopt{-X} en la línea de comandos, e intercalarlos como desee. +Por defecto, Mercurial interpreta los patrones que usted pase usando +la sintaxis glob (pero usted puede usar expresiones regulares si lo +necesita). + +El filtro \hggopt{-I} puede verse como un ``procese todos los ficheros +que coincidan con este filtro''. +\interaction{filenames.filter.include} +El filtro \hggopt{-X} puede verse como ``procese únicamente los +ficheros que no coincidan con este patrón''. +\interaction{filenames.filter.exclude} + +\section{Ignorar ficheros y directorios no deseados} + +XXX. + +\section{Sensibilidad a mayúsculas} +\label{sec:names:case} + +Si usted está trabajando en un ambiente de desarrollo mixto que +contiene tanto sistemas Linux (u otro Unix) y sistemas Mac o Windows, +debería tener en mente el hecho de que ellos tratan +%TODO FIXME seguir desde aqui, no tengo idea de como traducir case +%sensitivity +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{Almacenamiento portable y seguro de repositorios} + +El mecanismo de almacenamiento de los repositorios en Mercurial es +\emph{robusto frente a sensibilidad/insensibilidad a mayúsculas}. Los nombres de +fichero son traducidos para que puedan ser almacenados de manera +segura tanto en sistemas sensibles como insensibles a mayúsculas. Esto +significa que usted puede usar herramientas normales de copia de +ficheros para transferir un repositorio Mercurial a, por ejemplo, una +memoria USB, y trasladar de manera segura la memoria y el repositorio +de ida y vuelta entre un Mac, un PC ejecutando Windows, y un sistema +Linux + +\subsection{Detección de conflictos de mayúsculas/minúsculas} + +Al operar en el directorio de trabajo, Mercurial respeta la política +de nombrado del sistema de ficheros en que se encuentre el directorio +de trabajo. Si el sistema de ficheros conserva las diferencias entre +mayúsculas, pero no es sensible a ellas, Mercurial tratará los nombres +que sólo difieren en mayúsculas como uno solo y el mismo. + +Un aspecto importante de este enfoque es que es posible consignar un +conjunto de cambios en un sistema de ficheros sensible a mayúsculas +(típicamente Linux o Unix) que terminará causando problemas para +usuarios en sistemas insensibles a mayúsculas (usualmente en Windows o +MacOS). Si un usuario de Linux consigna cambios a dos ficheros, uno de +ellos llamado \filename{myfile.c} y el otro llamado \filename{MyFile.C}, +ambos serán almacenados correctamente en el repositorio. Y serán +representados correctamente como ficheros separados, en los +directorios de trabajo de otros usuarios de Linux. + +Si un usuario de Windows o Mac jalan este cambio, no tendrán problemas +inicialmente, porque el mecanismo de almacenamiento de Mercurial es +seguro frente a sensibilidad/insensibilidad a mayúsculas. Sin embargo, +una vez que ellos traten de actualizar (\hgcmd{update}) el directorio +de trabajo con ese conjunto de cambios, o hagan fusión (\hgcmd{merge}) +con ese conjunto de cambios, Mercurial verá el conflicto entre los dos +nombres de fichero que el sistema de ficheros trataría como el mismo, +e impedirá que ocurra la actualización o fusión. + +\subsection{Arreglar un conflicto de mayúsculas/minúsculas} + +Si usted está usando Windows o Mac en un entorno mixto donde algunos +de sus colaboradores están usando Linux o Unix, y Mercurial reporta un +conflicto de mayúsculas/minúsculas cuando usted trata de actualizar +(\hgcmd{update}) o fusionar (\hgcmd{merge}), el procedimiento para +arreglar el problema es simple. + +Sólo busque un sistema Linux o Unix cercano, clone el repositorio +problema allí, y use el comando \hgcmd{rename} de Mercurial para +cambiar los nombres de cualquiera de los ficheros o directorios +problemáticos para que no causen más conflictos. Consigne este cambio, +y jálelo (\hgcmd{pull}) o empújelo (\hgcmd{push}) a su sistema Windows +o MacOS, y actualícelo (\hgcmd{update}) a la revisión con los nombres +que ya no generan conflictos. + +El conjunto de cambios con los nombres con conflictos de +mayúsculas/minúsculas permanecerá en el historial de su proyecto, y +usted no podrá actualizar (\hgcmd{update}) su directorio de trabajo a +dicho conjunto de cambios en un sistema Windows o MacOS, pero puede +continuar el desarrollo sin impedimentos. + +\begin{note} + Antes de la versión~0.9.3, Mercurial no usaba un mecanismos seguro + frente a sensibilidad/insensibilidad a mayúsculas o minúsculas, y no + detectaba los conflictos con nombres de ficheros. Si usted está + usando una versión más antigua de Mercurial en Windows o MacOS, le + recomiendo enérgicamente que se actualice. +\end{note} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/fixhtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixhtml.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/fixhtml.py \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/fixsvg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixsvg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/fixsvg \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgbook.css Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/hgbook.css \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/hgext.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgext.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,441 @@ +\chapter{Añadir funcionalidad con extensiones} +\label{chap:hgext} + +A pesar de que el corazón de Mercurial es muy completo desde el punto +de vista de funcionalidad, carece de características rimbombantes +deliberadamente. Esta aproximación de preservar la simplicidad +mantiene el programa sencillo tanto para mantenedores como para +usuarios. + +Si embargo Mercurial no le cierra las posibilidades a un conjunto +inflexible de órdenes: usted puede añadir características como +\emph{extensiones} (aveces llamadas \emph{añadidos}\ndt{plugins}). Ya +hemos discutido algunas de estas extensiones en capítulos anteriores: +\begin{itemize} +\item La sección~\ref{sec:tour-merge:fetch} cubre la extensión + \hgext{fetch}; que combina jalar cambios y fusionarlos con los + cambios locales en una sola orden: \hgxcmd{fetch}{fetch}. +\item En el capítulo~\ref{chap:hook}, cubrimos muchas extensiones que + son útiles en funcionalidades relacionadas con ganchos: Los + \hgext{acl} añaden listas de control de acceso; \hgext{bugzilla} + añade integración con el sistema de seguimiento de fallos Bugzilla; y + \hgext{notify} envía notificaciones por correo de nuevos cambios. +\item La extensión de administración de parches MQ es tan invaluable + que amerita dos capítulos y un apéndice por sí misma. + El capítulo~\ref{chap:mq} cubre lo básico; el + capítulo~\ref{chap:mq-collab} discute temas avanzados; y el + apéndice~\ref{chap:mqref} muestra en detalle cada orden. +\end{itemize} + +En este capítulo cubriremos algunas extensiones adicionales +disponibles para Mercurial, y daremos un vistazo a la maquinaria que +necesita conocer en caso de que desee escribir una extensión. +\begin{itemize} +\item En la sección~\ref{sec:hgext:inotify}, discutiremos la + posibilidad de mejorar el desempeño \emph{en gran medida} con la extensión + \hgext{inotify}. +\end{itemize} + +\section{Mejorar el desempeño con la extensión \hgext{inotify}} +\label{sec:hgext:inotify} + +¿Desea lograr que las operaciones más comunmente usadas de Mercurial se +ejecuten centenas de veces más rápido? ¡A leer! + +Mercurial tiene gran desempeño bajo circunstancias normales. Por +ejemplo, cuando ejecuta la orden \hgcmd{status}, Mercurial tiene que +revisar casi todos los ficheros y directorios en su repositorio de +forma que pueda desplegar el estado de los ficheros. Muchas otras +órdenes tienen que hacer tal trabajo tras bambalinas; por ejemplo la +orden \hgcmd{diff} usa la maquinaria de estado para evitar hacer +operaciones de comparación costosas en ficheros que obviamente no han +cambiado. + +Dado que obtener el estado de los ficheros es crucial para obtener +buen desempeño, los autores de Mercurial han optimizado este código en +la medida de lo posible. Sin embargo, no puede obviarse el hecho de +que cuando ejecuta \hgcmd{status}, Mercurial tendrá que hacer por lo +menos una costosa llamada al sistema por cada fichero administrado +para determinar si ha cambiado desde la última vez que se consignó. +Para un repositorio suficientemente grande, puede tardar bastante +tiempo. + +Para mostrar en números la magnitud de este efect, creé un repositorio +que contenía 150.000 ficheros administrador. Tardó diez segundos para +ejecutar \hgcmd{status}, a pesar de que \emph{ninguno} de los ficheros +había sido modificado. + +Muchos sistemas operativos modernos contienen una facilidad de +notificación de ficheros. Si un programa se registra con un servicio +apropiado, el sistema operativo le notificará siempre que un fichero +de interés haya sido creado, modificado o borrado. En sistemas Linux, +el componente del núcleo que lo hace se llama \texttt{inotify}. + +La extensión \hgext{inotify} habla con el componente \texttt{inotify} +del núcleo para optimizar las órdenes de \hgcmd{status}. La extensión +tiene dos componentes. Un daemonio está en el fondo recibiendo +notificaciones del subsistema \texttt{inotify}. También escucha +conexiones de una orden regular de Mercurial. La extensión modifica +el comportamiento de Mercurial de tal forma que, en lugar de revisar +el sistema de ficheros, le pregunta al daemonio. Dado que el daemonio +tiene información perfecta acerca del estado del repositorio, puede +responder instantáneamente con el resultado, evitando la necesidad de +revisar cada directorio y fichero del repositorio. + +Retomando los diez segundos que medí al ejecutar la orden +\hgcmd{status} de Mercurial sobre un repositorio de 150.000 +ficheros. Con la extensión \hgext{inotify} habilitada, el tiempo se +disipó a 0.1~seconds, un factor \emph{cien veces} más rápido. + +Antes de continuar, tenga en cuenta algunos detalles: +\begin{itemize} +\item La extensión \hgext{inotify} es específica de Linux. Porque se + enlaza directamente con el subsistema \texttt{inotify} del núcleo + Linux, no funciona en otros sistemas operativos. +\item Debería funcionar en cualquier distribución Linux a partir de + comienzos del 2005. Las distribuciones más antiguas deben tener un + kernel sin \texttt{inotify}, o una versión de \texttt{glibc} que no + tiene necesariamente el soporte para la interfaz. +\item No todos los sistemas de ficheros pueden usarse con la extensión + \hgext{inotify}. Los sistemas de ficheros tales como NFS no lo + soportan, por ejemplo, si está corriendo Mercurial en vaios + sistemas, montados todos sobre el mismo sistema de ficheros en red. + El sistema \texttt{inotify} del kernel no tiene forma de saber + acerca de los cambios hechos en otro sistema. La mayoría de + sistemas de ficheros locales (p.e.~ext3, XFS, ReiserFS) deberían + funcionar bien. +\end{itemize} + +Hacia mayo de 2007 la extensión \hgext{inotify} no venía de forma +predeterminada en Mercurial\ndt{Desde el 2008 para kernels 2.6 viene + en Mercurial, pero no está activada de forma predeterminada}, y es +un poco más compleja de activar que otras extensiones. Pero la mejora +en el desempeño bien vale la pena! + +La extensión venía en dos partes: un conjunto de parches al código +fuente de Mercurial, y una librería de interfaces de Python hacia el +subsistema \texttt{inotify}. +\begin{note} + Hay \emph{dos} librerías de enlace de Python hacia \texttt{inotify}. + Una de ellas se llama \texttt{pyinotify}, y en algunas + distribuciones de Linux se encuentra como \texttt{python-inotify}. + Esta es la que \emph{no} necesita, puesto que tiene muchos fallos, + y es ineficiente para ser práctica. +\end{note} +Para comenzar, es mejor tener una copia de Mercurial funcional +instalada: +\begin{note} + Si sigue las instrucciones a continuación, estará + \emph{reemplazando} y sobreescribiendo cualquier instalación previa + de Mercurial que pudiera tener, con el código de Mercurial ``más + reciente y peligrosa''. No diga que no se le advirtio! +\end{note} +\begin{enumerate} +\item Clone el repositorio de interfaz entre Python e + \texttt{inotify}. Ármelo e instálelo: + \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 el repositorio \dirname{crew} de Mercurial. Clone el + repositorio de parches de \hgext{inotify} de forma tal que las colas + de Mercurial puedan aplicar los parches sobre el repositorio \dirname{crew}. + \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 Asegúrese de instalar la extensión Colas de Mercurial + \hgext{mq} y que estén habilitadas. Si nunca ha usado MQ, lea la + sección~\ref{sec:mq:start} para poder comenzar rápidamente. +\item Vaya al repositorio de \dirname{inotify} y aplique todos los + parches de \hgext{inotify} con la opción \hgxopt{mq}{qpush}{-a} de + la orden \hgxcmd{mq}{qpush}. + \begin{codesample4} + cd inotify + hg qpush -a + \end{codesample4} + Si obtiene un mensaje de error de \hgxcmd{mq}{qpush}, no debería + continuar. Mejor pida ayuda. +\item Arme e instale la versión parchada de Mercurial. + \begin{codesample4} + python setup.py build --force + sudo python setup.py install --skip-build + \end{codesample4} +\end{enumerate} +Una vez que haya armado una versión funcional parchada de Mercurial, +todo lo que necesita es habilitar la extensión \hgext{inotify} +colocando una entrada en su \hgrc. +\begin{codesample2} + [extensions] + inotify = +\end{codesample2} +Cuando la extensión \hgext{inotify} esté habilitada, Mercurial +iniciará transparente y automáticamente el daemonio de estado la +primera vez que ejecute un comando que requiera estado del +repositorio. Ejecuta un daemonio de estado por repositorio. + +El daemonio de estado se inicia silenciosamente y se ejecuta en el +fondo. Si mira a la lista de procesos en ejecución después de +habilitar la extensión \hgext{inotify} y ejecuta unos pocos comandos +en diferentes repositorios, verá que hay algunos procesos de +\texttt{hg} por ahí, esperando actualizaciones del kernel y +solicitudes de Mercurial. + +La primera vez que ejecuta un comando de Mercurial en un repositorio +cuando tiene la extensión \hgext{inotify} habilitada, correrá casi con +el mismo desempeño que una orden usual de Mercurial. Esto es debido a +que el estado del daemonio necesita aplicar una búsqueda normal sobre +el estado para poder tener una línea de partida frente a la cual +aplicar posteriormente actualizaciones del núcleo. De todas formas, +\emph{todo} comando posterior que haga cualquier clase de revisión del +estado debería ser notablemente más rápido en repositorios con incluso +un tamaño modesto. Aún mejor, a medida que su repositorio sea más +grande, mejor desempeño verá. El daemonio \hgext{inotify} hace +operaciones de estado de forma casi instantánea en repositorios de +todos los tamaños! + +Si lo desea, puede iniciar manualmente un daemonio de estado con la orden +\hgxcmd{inotify}{inserve}. Esto le da un control un poco más fino +acerca de cómo debería ejecutarse el daemonio. Esta orden solamente +estará disponible cuando haya habilitado la extensión \hgext{inotify}. + +Cuando esté usando la extensión \hgext{inotify}, +\emph{no debería ver diferencia} en el comportamiento de Mercurial, +con la única excepción de que los comandos relacionados con el estado +deberían ejectuarse mucho más rápido que como solían hacerlo. Debería +esperar específicamente que las órdenes no deberían ofrecer salidas +distintas; ni ofrecer resultados diferentes. Si alguna de estas +situaciones ocurre, por favor reporte el fallo. + +\section{Soporte flexible de diff con la extensión \hgext{extdiff}} +\label{sec:hgext:extdiff} + +La orden predeterminada \hgcmd{diff} de Mercurial despliega diffs en +texto plano unificadas. +\interaction{extdiff.diff} +Si dese emplear una herramienta externa para desplegar las +modificaciones, querrá usar la extensión \hgext{extdiff}. Esta le +permitirá usar por ejemplo una herramienta gráfica de diff. + +La extensión \hgext{extdiff} viene con Mercurial, y es fácil +configurar. En la sección \rcsection{extensions} de su \hgrc, +basta con añadir una entrada de una línea para habilitar la extensión. +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} +Esto introduce una orden llamada \hgxcmd{extdiff}{extdiff}, que de +forma predeterminada usa su orden del sistema \command{diff} para +generar un diff unificado de la misma forma que lo hace el comando +predeterminado \hgcmd{diff}. +\interaction{extdiff.extdiff} +El resultado no será exactamente el mismo que con la orden interna +\hgcmd{diff}, puesto que la salida de \command{diff} varía de un +sistema a otro, incluso pasando las mismas opciones. + +Como lo indican las líneas``\texttt{making snapshot}'', la orden +\hgxcmd{extdiff}{extdiff} funciona creando dos instantáneas de su +árbol de fuentes. La primera instantánea es la revisión fuente; la +segunda es la revisión objetivo del directorio de trabajo. La orden +\hgxcmd{extdiff}{extdiff} genera estas instantáneas en un directorio +temporal, pasa el nombre de cada directorio a un visor de diffs +temporal y borra los directorios temporales. Por cuestiones de +eficiencia solamente genera instantáneas de los directorios y ficheros +que han cambiado entre dos revisiones. + +Los nombres de los directorios de instantáneas tienen los mismos +nombres base de su repositorio. Si su repositorio tiene por ruta +\dirname{/quux/bar/foo}, \dirname{foo} será el nombre de cada +instantánea de directorio. Cada instantánea de directorio tiene sus +identificadores de conjuntos de cambios al final del nombre en caso de +que sea apropiado. Si una instantánea viene de la revisión +\texttt{a631aca1083f}, el directorio se llamará +\dirname{foo.a631aca1083f}. Una instantánea del directorio de trabajo +no tendrá el identificador del conjunto de cambios, y por lo tanto +será solamente \dirname{foo} en este ejemplo. Para ver cómo luce en +la práctica, veamos de nuevo el ejemplo \hgxcmd{extdiff}{extdiff} +antes mencionado. Tenga en cuenta que los diffs tienen los nombres de +las instantáneas de directorio dentro de su encabezado. + +La orden \hgxcmd{extdiff}{extdiff} acepta dos opciones importantes. +La opción \hgxopt{extdiff}{extdiff}{-p} le permite elegir un programa +para ver las diferencias, en lugar de \command{diff}. Con la opción +\hgxopt{extdiff}{extdiff}{-o} puede cambiar las opciones que +\hgxcmd{extdiff}{extdiff} pasa a tal programa (de forma predeterminada +las opciones son``\texttt{-Npru}'', que tienen sentido únicamente si +está usando \command{diff}). En otros aspectos, la orden +\hgxcmd{extdiff}{extdiff} actúa de forma similar a como lo hace la +orden \hgcmd{diff} de Mercurial: usted usa los mismos nombres de +opciones, sintaxis y argumentos para especificar las revisiones y los +ficheros que quiere, y así sucesivamente. + +Por ejemplo, para ejecutar la orden usual del sistema \command{diff}, +para lograr que se generen diferencias de contexto (con la opción +\cmdopt{diff}{-c}) en lugar de diferencias unificadas, y cinco líneas +de contexto en lugar de las tres predeterminadas (pasando \texttt{5} +como argumento a la opción \cmdopt{diff}{-C}). +\interaction{extdiff.extdiff-ctx} + +Es sencillo lanzar unas herramienta usual de diferencias. Para lanzar +el visor \command{kdiff3}: +\begin{codesample2} + hg extdiff -p kdiff3 -o '' +\end{codesample2} + +Si su orden para visualizar diferencias no puede tratar con +directorios, puede usar un poco de scripting para lograrlo. Un +ejemplo de un script con la extensión \hgext{mq} junto con la orden +\command{interdiff} está en la sección~\ref{mq-collab:tips:interdiff}. + +\subsection{Definición de alias de comandos} + +Acordarse de todas las opciones de las órdenes +\hgxcmd{extdiff}{extdiff} y el visor de diferencias de su preferencia +puede ser dispendioso, y por lo tanto la extensión \hgext{extdiff} le +permite definir \emph{nuevas} órdenes que invocarán su visor de +diferencias con las opciones exactas. + +Basta con editar su fichero \hgrc, y añadir una sección llamada +\rcsection{extdiff}. Dentro de esta sección puede definir varias +órdenes. Mostraremos como añadir la orden \texttt{kdiff3}. Después de +definido, puede teclear ``\texttt{hg kdiff3}'' y la extensión a +\hgext{extdiff} ejecutará la orden \command{kdiff3}. +\begin{codesample2} + [extdiff] + cmd.kdiff3 = +\end{codesample2} +Si deja vacía la porción derecha de la definición, como en el ejemplo, +la extensión \hgext{extdiff} usa el nombre de la orden se definirá +como el nombre del programa externo a ejecutar. Pero tales nombres no +tienen por qué ser iguales. Definimos ahora la orden llamada + ``\texttt{hg wibble}'', que ejecuta \command{kdiff3}. +\begin{codesample2} + [extdiff] + cmd.wibble = kdiff3 +\end{codesample2} + +También puede especificar las opciones predeterminadas con las cuales +desea invocar el visor de diferencias. Se usa el prefijo ``\texttt{opts.}'', +seguido por el nombre de la orden a la cual se aplican las opciones. +En este ejemplos se define la orden ``\texttt{hg vimdiff}'' que +ejecuta la extensión \texttt{DirDiff} del editor \command{vim}. +\begin{codesample2} + [extdiff] + cmd.vimdiff = vim + opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' +\end{codesample2} + +\section{Uso de la extensión \hgext{transplant} para seleccionar} +\label{sec:hgext:transplant} + +Need to have a long chat with Brendan about this. + +\section{Enviar cambios vía correo electrónico con la extensión \hgext{patchbomb}} +\label{sec:hgext:patchbomb} + +Varios proyectos tienen la cultura de ``revisión de cambios'', en la +cual la gente envía sus modificaciones a una lista de correo para que +otros las lean y comenten antes de consignar la versión final a un +repositorio compartido. Algunos proyectos tienen personas que actúan +como cancerberos; ellos aplican los cambios de otras personas a un +repositorio para aquellos que no tienen acceso. + +Mercurial facilita enviar cambios por correo para revisión o +aplicación gracias a su extensión \hgext{patchbomb}. La extensión es +tan popular porque los cambios se formatean como parches y es usual +que se envía un conjunto de cambios por cada correo. Enviar una gran +cantidad de cambios por correos se llama por tanto ``bombardear'' el +buzón de entrada del destinatario, de ahí su nombre ``bombardeo de +parches''. + +Como es usual, la configuración básica de la extensión +\hgext{patchbomb} consta de una o dos líneas en su \hgrc. +\begin{codesample2} + [extensions] + patchbomb = +\end{codesample2} +Cuando haya habilitado la extensión, dispondrá de una nueva orden, +llamada \hgxcmd{patchbomb}{email}. + +La forma mejor y más segura para invocar la orden +\hgxcmd{patchbomb}{email} es ejecutarla \emph{siempre} con la opción +\hgxopt{patchbomb}{email}{-n}; que le mostrará lo que la orden +\emph{enviaría}, sin enviar nada. Una vez que haya dado un vistazo a +los cambios y verificado que está enviando los correctos, puede volver +a ejecutar la misma orden, sin la opción \hgxopt{patchbomb}{email}{-n}. + +La orden \hgxcmd{patchbomb}{email} acepta la misma clase de sintaxis +de revisiones como cualquier otra orden de Mercurial. Por ejemplo, +enviará todas las revisiones entre la 7 y la \texttt{punta}, inclusive. +\begin{codesample2} + hg email -n 7:tip +\end{codesample2} +También puede especificar un \emph{repositorio} para comparar. Si +indica un repositoro sin revisiones, la orden \hgxcmd{patchbomb}{email} +enviará todas las revisiones en el repositorio local que no están +presentes en el repositorio remoto. Si especifica revisiones +adicionalmente o el nombre de una rama (la última con la opción +\hgxopt{patchbomb}{email}{-b}), respetará las revisiones enviadas. + +Ejecutar la orden \hgxcmd{patchbomb}{email} sin los nombres de +aquellas personas a las cuales desea enviar el correo es completamente +seguro: si lo hace, solicitará tales valores de forma interactiva. +(Si está usando Linux o un sistema tipo Unix, tendrá capacidades +estilo--\texttt{readline} aumentadas cuando ingrese tales encabezados, +lo cual es sumamente útil.) + +Cuando envíe una sola revisión, la orden \hgxcmd{patchbomb}{email} +de forma predeterminada usará la primera línea de descripción del +conjunto de cambios como el tema del único mensaje que se enviará. + +Si envía varias revisiones, la orden \hgxcmd{patchbomb}{email} enviará +normalmente un mensaje por conjunto de cambios. Colocará como +prefacio un mensaje introductorio en el cual usted debería describir +el propósito de la serie de cambios que está enviando. + +\subsection{Cambiar el comportamiento de las bombas de parches} + +Cada proyecto tiene sus propias convenciones para enviar cambios en un +correo electrónico; la extensión \hgext{patchbomb} intenta acomodarse +a diferentes variaciones gracias a las opciones de la línea de órdenes: +\begin{itemize} +\item Puede escribir un tema para el mensaje introductorio en la línea + de órdenes con la opciń \hgxopt{patchbomb}{email}{-s}. Toma un + argumento: el tema del mensaje a usar. +\item Para cambiar el correo electrónico del campo del cual se + origina, use la opción \hgxopt{patchbomb}{email}{-f}. Toma un + argumento, el correo electrónico a usar. +\item El comportamiento predeterminado es enviar diferencias + unificadas (consulte la sección~\ref{sec:mq:patch} si desea una + descripción del formato), una por mensaje. Puede enviar un conjunto + binario\ndt{binary bundle} con la opción \hgxopt{patchbomb}{email}{-b}. +\item Las diferencias unificadas están precedidas por un encabezado de + metadatos. Puede omitirlo, y enviar diferencias sin adornos con la + opción \hgxopt{patchbomb}{email}{--plain}. +\item Las diferencias usualmente se envían ``en línea'', como parte + del cuerpo del mensaje con la descripción del parche. Que facilita a + a la mayor cantidad de lectores citar y responder partes de un diff, + dado que algunos clientes de correo solamente citarán la primera + parte MIME del cuerpo de un mensaje. Si prefiere enviar la + descripción y el diff en partes separadas del cuerpo, use la opción + \hgxopt{patchbomb}{email}{-a}. +\item En lugar de enviar mensajes de correo puede escribirlos a un + fichero con formato-\texttt{mbox}- con la opción + \hgxopt{patchbomb}{email}{-m}. La opción recibe un argumento, el + nombre del fichero en el cual escribir. +\item Si desea añadir un resumen con formato-\command{diffstat} en + cada parche, y uno como mensaje introductorio, use la opción + \hgxopt{patchbomb}{email}{-d}. La orden \command{diffstat} + despliega una tabla que contiene el nombre de cada fichero parchado, + el número de líneas afectadas, y un historgrama mostrando cuánto ha + sido modificado cada fichero. Lo cual ofrece a los lectores una + mirada cuantitativa de cuan complejo es el parche. +\end{itemize} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/hook.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hook.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1568 @@ +\chapter{Manejo de eventos en repositorios mediante ganchos} +\label{chap:hook} + +Mercurial ofrece un poderoso mecanismo para permitirle a usted +automatizar la ejecución de acciones en respuesta a eventos que +ocurran en un repositorio. En algunos casos, usted puede controlar +incluso la respuesta de Mercurial a dichos eventos. + +Mercurial usa el término \emph{gancho} para identificar estas +acciones. Los ganchos son conocidos como ``disparadores'' en algunos +sistemas de control de revisiones, pero los dos nombres se refieren al +mismo concepto. + +\section{Vistazo general de ganchos en Mercurial} + +A continuación se encuentra una breve lista de los ganchos que +Mercurial soporta. Volveremos a cada uno de estos ganchos con más +detalle después, en la sección~\ref{sec:hook:ref}. + +\begin{itemize} +\item[\small\hook{changegroup}] Es ejecutado luego de que un grupo de + conjuntos de cambios ha sido traído al repositorio desde algún + otro sitio. +\item[\small\hook{commit}] Es ejecutado después de la creación de + un conjunto de cambios en el repositorio local. +\item[\small\hook{incoming}] Es ejecutado una vez por cada conjunto de + cambios traído al repositorio desde otra ubicación. Note la + diferencia respecto al gancho \hook{changegroup}, que es ejecutado + una vez por cada \emph{grupo} de conjuntos de cambios que se + traiga. +\item[\small\hook{outgoing}] Es ejecutado luego de que un grupo de + conjuntos de cambios ha sido transmitido desde el repositorio. +\item[\small\hook{prechangegroup}] Es ejecutado antes de iniciar la + recepción de un grupo de conjuntos de cambios en el repositorio. +\item[\small\hook{precommit}] De control. Es ejecutado antes de + iniciar una consignación. +\item[\small\hook{preoutgoing}] De control. Es ejecutado antes de + iniciar la transmisión de un grupo de conjuntos de cambios desde + el repositorio. +\item[\small\hook{pretag}] De control. Es ejecutado antes de crear una + etiqueta. +\item[\small\hook{pretxnchangegroup}] De control. Es ejecutado después + de haber recibido un grupo de conjuntos de cambios en el + repositorio local, pero antes de que la transacción se complete y + los cambios sean permanentes dentro del repositorio. +\item[\small\hook{pretxncommit}] De control. Es ejecutado luego de la + creación de un conjunto de cambios en el repositorio local, pero + antes de que la transacción que hace permanente el cambio sea + completada. +\item[\small\hook{preupdate}] De control. Es ejecutado antes de + iniciar una actualización o fusión en el directorio de trabajo. +\item[\small\hook{tag}] Es ejecutado después de la creación de una + etiqueta. +\item[\small\hook{update}] Es ejecutado después de que termina una + actualización o una fusión. +\end{itemize} +Cada uno de los ganchos cuya descripción empieza con la frase +``de control'' tiene la facultad de determinar si una actividad puede +continuar. Si el gancho se ejecuta con éxito, la actividad puede +continuar; si falla, o bien la actividad no es permitida, o se +deshacen los cambios que se puedan haber llevado a cabo, dependiendo +del gancho involucrado. + +\section{Ganchos y seguridad} + +\subsection{Los ganchos se ejecutan con sus privilegios de usuario} + +Cuando usted ejecuta un comando de Mercurial en un repositorio, y el +comando causa la ejecución de un gancho, dicho gancho se ejecuta en +\emph{su} sistema, en \emph{su} cuenta de usuario, con \emph{sus} +privilegios. Ya que los ganchos son elementos arbitrarios de código +ejecutable, usted debería tratarlos con un nivel adecuado de +desconfianza. No instale un gancho a menos en que confíe en quien lo +creó y en lo que el gancho hace. + +En algunos casos, usted puede estar expuesto a ganchos que usted no +%TODO acá introduzco algo de texto por mi cuenta, por claridad +instaló. Si usted usa Mercurial en un sistema extraño, tenga en cuenta +que Mercurial ejecutará los ganchos definidos en el fichero \hgrc. + +Si está trabajando con un repositorio propiedad de otro usuario, +Mercurial podrá ejecutar los ganchos definidos en el repositorio de +dicho usuario, pero los ejecutará como ``usted''. Por ejemplo, si +usted jala (\hgcmd{pull}) desde ese repositorio, y el +\sfilename{.hg/hgrc} define un gancho saliente (\hook{outgoing}), +dicho gancho se ejecuta bajo su cuenta de usuario, aun cuando usted no +es el propietario del repositorio. + +\begin{note} + Esto sólo aplica si usted está jalando desde un repositorio en un + sistema de ficheros local o de red. Si está jalando a través de http + o ssh, cualquier gancho saliente (\hook{outgoing}) se ejecutará bajo + la cuenta que está ejecutando el proceso servidor, en el servidor. +\end{note} + +XXX Para ver qué ganchos han sido definidos en un repositorio, use el +comando \hgcmdargs{config}{hooks}. Si usted está trabajando en un +repositorio, pero comunicándose con otro que no le pertenece +(por ejemplo, usando \hgcmd{pull} o \hgcmd{incoming}), recuerde que +los ganchos que debe considerar son los del otro repositorio, no los +del suyo. + +\subsection{Los ganchos no se propagan} + +En Mercurial, no se hace control de revisiones de los ganchos, y no se +propagan cuando usted clona, o jala de, un repositorio. El motivo para +esto es simple: un gancho es código ejecutable arbitrario. Se ejecuta +bajo su identidad, con su nivel de privilegios, en su máquina. + +Sería extremadamente descuidado de parte de cualquier sistema +distribuido de control de revisiones el implementar control de +revisiones para ganchos, ya que esto ofrecería maneras fácilmente +%TODO subvertir +aprovechables de subvertir las cuentas de los usuarios del sistema de +control de revisiones. + +Ya que Mercurial no propaga los ganchos, si usted está colaborando con +otras personas en un proyecto común, no debería asumir que ellos están +usando los mismos ganchos para Mercurial que usted usa, o que los de +ellos están configurado correctamente. Usted debería documentar los +ganchos que usted espera que la gente use. + +En una intranet corporativa, esto es algo más fácil de manejar, ya que +usted puede, por ejemplo, proveer una instalación ``estándar'' de +Mercurial en un sistema de ficheros NFS, y usar un fichero \hgrc\ +global para definir los ganchos que verán todos los usuarios. Sin +embargo, este enfoque tiene sus límites; vea más abajo. + +\subsection{Es posible hacer caso omiso de los ganchos} + +Mercurial le permite hacer caso omiso de la deficinión de un gancho, +a través de la redefinición del mismo. Usted puede deshabilitar el +gancho fijando su valor como una cadena vacía, o cambiar su +comportamiento como desee. + +Si usted instala un fichero \hgrc\ a nivel de sistema o sitio completo +que define algunos ganchos, debe entender que sus usuarios pueden +deshabilitar o hacer caso omiso de los mismos. + +\subsection{Asegurarse de que ganchos críticos sean ejecutados} + +Algunas veces usted puede querer hacer respetar una política, y no +permitir que los demás sean capaces de evitarla. Por ejemplo, usted +puede tener como requerimiento que cada conjunto de cambios debe pasar +un riguroso conjunto de pruebas. Definir este requerimientos a través +de un gancho en un fichero \hgrc\ global no servirá con usuarios +remotos en computadoras portátiles, y por supuesto que los usuarios +locales pueden evitar esto a voluntad haciendo caso omiso del gancho. + +En vez de eso, usted puede definir las políticas para usar Mercurial +de tal forma que se espere que los usuarios propaguen los cambios a +través de un servidor ``canónico'' bien conocido que usted ha +asegurado y configurado apropiadamente. + +Una manera de hacer esto es a través de una combinación de ingeniería +social y tecnología. Cree una cuenta de acceso restringido; los +usuarios pueden empujar cambios a través de la red a los repositorios +administrados por esta cuenta, pero no podrán ingresar a dicha cuenta +para ejecutar órdenes en el intérprete de comandos. En este escenario, +un usuario puede enviar un conjunto de cambios que contenga la +porquería que él desee. + +Cuando alguien empuja un conjunto de cambios al servidor del que todos +jalan, el servidor probará el conjunto de cambios antes de aceptarlo +como permanente, y lo rechazará si no logra pasar el conjunto de +pruebas. Si la gente sólo jala cambios desde este servidor de filtro, +servirá para asegurarse de que todos los cambios que la gente jala han +sido examinados automáticamente + +\section{Precauciones con ganchos \texttt{pretxn} en un repositorio de +acceso compartido} + +Si usted desea usar ganchos para llevar a cabo automáticamente algún +trabajo en un repositorio al que varias personas tienen acceso +compartido, debe tener cuidado con la forma de hacerlo. + +Mercurial sólo bloquea un repositorio cuando está escribiendo al +mismo, y sólo las partes de Mercurial que escriben al repositorio le +prestan atención a los bloqueos. Los bloqueos de escritura son +necesarios para evitar que múltiples escritores simultáneos +interfieran entre sí, corrompiendo el repositorio. + +Ya que Mercurial tiene cuidado con el orden en que lee y escribe +datos, no necesita adquirir un bloqueo cuando desea leer datos del +repositorio. Las partes de Mercurial que leen del repositorio nunca le +prestan atención a los bloqueos. Este esquema de lectura libre de +bloqueos incremententa en gran medida el desempeño y la concurrencia. + +Sin embargo, para tener un gran desempeño es necesario hacer +sacrificios, uno de los cuales tiene el potencial de causarle +problemas a menos de que usted esté consciente de él. Describirlo +requiere algo de detalle respecto a cómo Mercurial añade conjuntos de +cambios al repositorio y cómo lee esos cambios de vuelta. + +Cuando Mercurial \emph{escribe} metadatos, los escribe directamente en +el fichero de destino. Primero escribe los datos del fichero, luego +los datos del manifiesto (que contienen punteros a los nuevos datos +del fichero), luego datos de la bitácora de cambios (que contienen +punteros a los nuevos datos del manifiesto). Antes de la primera +escritura a cada fichero, se guarda un registro de dónde estaba el +final de fichero en su registro de transacciones. Si la transacción +debe ser deshecha, Mercurial simplemente trunca cada fichero de vuelta +al tamaño que tenía antes de que empezara la transacción. + +Cuando Mercurial \emph{lee} metadatos, lee la bitácora de cambios +primero, y luego todo lo demás. Como un lector sólo accederá a las +partes del manifiesto o de los metadatos de fichero que él puede ver +en la bitácora de cambios, nunca puede ver datos parcialmente +escritos. + +Algunos ganchos de control (\hook{pretxncommit} y +\hook{pretxnchangegroup}) se ejecutan cuando una transacción está casi +completa. Todos los metadatos han sido escritos, pero Mercurial aún +puede deshacer la transacción y hacer que los datos recién escritos +desaparezcan. + +Si alguno de estos ganchos permanece en ejecución por mucho tiempo, +abre una ventana de tiempo en la que un lector puede ver los metadatos +de conjuntos de cambios que aún no son permanentes y que no debería +considerarse que estén ``realmante ahí''. Entre más tiempo tome la +ejecución del gancho, más tiempo estará abierta esta ventana. + +\subsection{Ilustración del problema} + +En principio, un buen uso del gancho \hook{pretxnchangegroup} sería +ensamblar y probar automáticamente todos los cambios entrantes antes +de que sean aceptados en un repositorio central. Esto le permitiría a +usted garantizar que nadie pueda empujar cambios que ``rompan el +ensamblaje''. Pero si un cliente puede jalar cambios mientras están +siendo probados, la utilidad de esta prueba es nula; alguien confiado +puede jalar cambios sin probar, lo que potencialmente podría romper su +proceso de ensamblaje. + +La respuesta técnica más segura frente a este retos es montar dicho +repositorio ``guardián'' como \emph{unidireccional}. Permita que +reciba cambios desde el exterior, pero no permita que nadie jale +cambios de él (use el gancho \hook{preoutgoing} para bloquear esto). +Configure un gancho \hook{changegroup} para que si el ensamblaje o +prueba tiene éxito, el gancho empuje los nuevos cambios a otro +repositorio del que la gente \emph{pueda} jalar. + +En la práctica, montar un cuello de botella centralizado como éste a +menudo no es una buena idea, y la visibilidad de las transacciones no +tiene nada que ver con el problema. A medida que el tamaño de un +proyecto---y el tiempo que toma ensamblarlo y probarlo---crece, usted +se acerca rápidamente a un límite con este enfoque ``pruebe antes de +comprar'', en el que tiene más conjuntos de cambios a probar que +tiempo para ocuparse de ellos. El resultado inevitable es frustración +para todos los que estén involucrados. + +Una aproximación que permite manejar mejor el crecimiento es hacer que +la gente ensamble y pruebe antes de empujar, y ejecutar el ensamble y +pruebas automáticas centralmente \emph{después} de empujar, para +asegurarse de que todo esté bien. La ventaja de este enfoque es que no +impone un límite a la rata en la que un repositorio puede aceptar +cambios. + +\section{Tutorial corto de uso de ganchos} +\label{sec:hook:simple} + +Escribir un gancho para Mercurial es fácil. Empecemos con un gancho +que se ejecute cuando usted termine un \hgcmd{commit}, y simplemente +muestre el hash del conjunto de cambios que usted acaba de crear. El +gancho se llamará \hook{commit}. + +\begin{figure}[ht] + \interaction{hook.simple.init} + \caption{Un gancho simple que se ejecuta al hacer la consignación de + un conjunto de cambios} + \label{ex:hook:init} +\end{figure} + +Todos los ganchos siguen el patrón del ejemplo~\ref{ex:hook:init}. +Usted puede añadir una entrada a la sección \rcsection{hooks} de su +fichero \hgrc. A la izquierda está el nombre del evento respecto al +cual dispararse; a la derecha está la acción a llevar a cabo. Como +puede ver, es posible ejecutar cualquier orden de la línea de comandos +en un gancho. Mercurial le pasa información extra al gancho usando +variables de entorno (busque \envar{HG\_NODE} en el ejemplo). + +\subsection{Llevar a cabo varias acciones por evento} + +A menudo, usted querrá definir más de un gancho para un tipo de evento +particular, como se muestra en el ejemplo~\ref{ex:hook:ext}. +Mercurial le permite hacer esto añadiendo una \emph{extensión} al +final del nombre de un gancho. Usted extiende el nombre del gancho +%TODO Yuk, no me gusta ese "parada completa" +poniendo el nombre del gancho, seguido por una parada completa (el +caracter ``\texttt{.}''), seguido de algo más de texto de su elección. +Por ejemplo, Mercurial ejecutará tanto \texttt{commit.foo} como +\texttt{commit.bar} cuando ocurra el evento \texttt{commit}. + +\begin{figure}[ht] + \interaction{hook.simple.ext} + \caption{Definición de un segundo gancho \hook{commit}} + \label{ex:hook:ext} +\end{figure} + +Para dar un orden bien definido de ejecución cuando hay múltiples +ganchos definidos para un evento, Mercurial ordena los ganchos de +acuerdo a su extensión, y los ejecuta en dicho orden. En el ejemplo de +arribam \texttt{commit.bar} se ejecutará antes que +\texttt{commit.foo}, y \texttt{commit} se ejecutará antes de ambos. + +Es una buena idea usar una extensión descriptiva cuando usted define +un gancho. Esto le ayudará a recordar para qué se usa el gancho. Si el +gancho falla, usted recibirá un mensaje de error que contiene el +nombre y la extensión del gancho, así que usar una extensión +descriptiva le dará una pista inmediata de porqué el gancho falló (vea +un ejemplo en la sección~\ref{sec:hook:perm}). + +\subsection{Controlar cuándo puede llevarse a cabo una actividad} +\label{sec:hook:perm} + +En los ejemplos anteriores, usamos el gancho \hook{commit}, que es +ejecutado después de que se ha completado una consignación. Este es +uno de los varios ganchos que Mercurial ejecuta luego de que una +actividad termina. Tales ganchos no tienen forma de influenciar la +actividad como tal. + +Mercurial define un número de eventos que ocurren antes de que una +actividad empiece; o luego de que empiece, pero antes de que termine. +Los ganchos que se disparan con estos eventos tienen la capacidad +adicional de elegir si la actividad puede continuar, o si su ejecución +es abortada. + +El gancho \hook{pretxncommit} se ejecuta justo antes de que una +consignación se ejecute. En otras palabras, los metadatos que +representan el conjunto de cambios han sido escritos al disco, pero no +se ha terminado la transacción. El gancho \hook{pretxncommit} tiene la +capacidad de decidir si una transacción se completa, o debe +deshacerse. + +Si el gancho \hook{pretxncommit} termina con un código de salida de +cero, se permite que la transacción se complete; la consignación +termina; y el gancho \hook{commit} es ejecutado. Si el gancho +\hook{pretxncommit} termina con un código de salida diferente de cero, +la transacción es revertida; los metadatos representando el conjunto +de cambios son borrados; y el gancho \hook{commit} no es ejecutado. + +\begin{figure}[ht] + \interaction{hook.simple.pretxncommit} + \caption{Uso del gancho \hook{pretxncommit} para controlar consignaciones} + \label{ex:hook:pretxncommit} +\end{figure} + +El gancho en el ejemplo~\ref{ex:hook:pretxncommit} revisa si el +mensaje de consignación contiene el ID de algún fallo. Si lo contiene, +la consignación puede continuar. Si no, la consignación es cancelada. + +\section{Escribir sus propios ganchos} + +Cuando usted escriba un gancho, puede encontrar útil el ejecutar +Mercurial o bien pasándole la opción \hggopt{-v}, o con el valor de +configuración \rcitem{ui}{verbose} fijado en ``true'' (verdadero). +Cuando lo haga, Mercurial imprimirá un mensaje antes de llamar cada +gancho. + +\subsection{Escoger cómo debe ejecutarse su gancho} +\label{sec:hook:lang} + +Usted puede escribir un gancho que funcione como un programa normal +---típicamente un guión de línea de comandos---o como una función de +Python que se ejecuta dentro del proceso Mercurial. + +Escribir un gancho como un programa externo tiene la ventaja de que no +requiere ningún conocimiento del funcionamiento interno de Mercurial. +Usted puede ejecutar comandos Mercurial normales para obtener la +informción extra que pueda necesitar. La contraparte de esto es que +los ganchos externos son más lentos que los ganchos internos +ejecutados dentro del proceso. + +Un gancho Python interno tiene acceso completo a la API de Mercurial, +y no se ``externaliza'' a otro proceso, así que es inherentemente más +rápido que un gancho externo. Adicionalmente es más fácil obtener la +mayoría de la información que un gancho requiere a través de llamadas +directas a la API de Mercurial que hacerlo ejecutando comandos +Mercurial. + +Si se siente a gusto con Python, o requiere un alto desempeño, +escribir sus ganchos en Python puede ser una buena elección. Sin +embargo, cuando usted tiene un gancho bastante directo por escribir y +no le importa el desempeño (el caso de la mayoría de los ganchos), es +perfectamente admisible un guión de línea de comandos. + +\subsection{Parámetros para ganchos} +\label{sec:hook:param} + +Mercurial llama cada gancho con un conjunto de paŕametros bien +definidos. En Python, un parámetro se pasa como argumento de palabra +clave a su función de gancho. Para un programa externo, los parámetros +son pasados como variables de entornos. + +Sin importar si su gancho está escrito en Python o como guión de línea +de comandos, los nombres y valores de los parámetros específicos de +los ganchos serán los mismos. Un parámetro booleano será representado +como un valor booleano en Python, pero como el número 1 (para +``verdadero'') o 0 (para falso) en una variable de entorno para un +gancho externo. Si un parámetro se llama \texttt{foo}, el argumento de +palabra clave para un gancho en Python también se llamará +\texttt{foo}, mientras que la variable de entorno para un gancho +externo se llamará \texttt{HG\_FOO}. + +\subsection{Valores de retorno de ganchos y control de actividades} + +Un gancho que se ejecuta exitosamente debe terminar con un código de +salida de cero, si es externo, o retornar el valor booleano +``falso'', si es interno. Un fallo se indica con un código de salida +diferente de cero desde un gancho externo, o un valor de retorno +booleano ``verdadero''. Si un gancho interno genera una excepción, se +considera que el gancho ha fallado. + +Para los ganchos que controlan si una actividad puede continuar o no, +cero/falso quiere decir ``permitir'', mientras que +% TODO me suena mejor "no permitir" que "denegar" +no-cero/verdadero/excepción quiere decir ``no permitir''. + +\subsection{Escribir un gancho externo} + +Cuando usted define un gancho externo en su fichero \hgrc\ y el mismo +es ejecutado, dicha definición pasa a su intérprete de comandos, que +hace la interpretación correspondiente. Esto significa que usted puede +usar elementos normales del intérprete en el cuerpo del gancho. + +Un gancho ejecutable siempre es ejecutado con su directorio actual +fijado al directorio raíz del repositorio. + +Cada parámetro para el gancho es pasado como una variable de entorno; +el nombre está en mayúsculas, y tiene como prefijo la cadena +``\texttt{HG\_}''. + +Con la excepción de los parámetros para los ganchos, Mercurial no +define o modifica ninguna variable de entorno al ejecutar un gancho. +Es útil recordar esto al escribir un gancho global que podría ser +ejecutado por varios usuarios con distintas variables de entorno +fijadas. En situaciones con múltiples usuarios, usted no debería +asumir la existencia de ninguna variable de entorno, ni que sus +valores sean los mismos que tenían cuando usted probó el gancho en su +ambiente de trabajo. + +\subsection{Indicar a Mercurial que use un gancho interno} + +La sintaxis para definir un gancho interno en el fichero \hgrc\ es +ligeramente diferente de la usada para un gancho externo. El valor del +gancho debe comenzar con el texto ``\texttt{python:}'', y continuar +con el nombre completamente cualificado de un objeto invocable que se +usará como el valor del gancho. + +El módulo en que vive un gancho es importado automáticamente cuando se +ejecuta un gancho. Siempre que usted tenga el nombre del módulo y la +variable de entorno \envar{PYTHONPATH} ajustada adecuadamente, todo +debería funcionar sin problemas. + +El siguiente fragmento de ejemplo de un fichero \hgrc\ ilustra la +sintaxis y significado de los conceptos que acabamos de describir. +\begin{codesample2} + [hooks] + commit.example = python:mymodule.submodule.myhook +\end{codesample2} +Cuando Mercurial ejecuta el gancho \texttt{commit.example}, importa +\texttt{mymodule.submodule}, busca el objeto invocable llamado +\texttt{myhook}, y lo invoca (llama). + +\subsection{Escribir un gancho interno} + +El gancho interno más sencillo no hace nada, pero ilustra la +estructura básica de la API\ndt{\emph{Application Progamming +Interface}, Interfaz para Programación de Aplicaciones} para ganchos: +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} +El primer argumento para un gancho Python siempre es un objeto +\pymodclass{mercurial.ui}{ui}. El segundo es un objeto repositorio; +de momento, siempre es una instancia de +\pymodclass{mercurial.localrepo}{localrepository}. Después de estos +dos argumentos están los argumentos de palabra clave. Los argumentos +que se pasen dependerán del tipo de gancho que se esté llamando, pero +un gancho siempre puede ignorar los argumentos que no le interesen, +relegándolos a un diccionario de argumentos por palabras clave, como se +hizo arriba con \texttt{**kwargs}. + +\section{Ejemplos de ganchos} + +\subsection{Escribir mensajes de consignación significativos} + +Es difícil de imaginar un mensaje de consignación útil y al mismo +tiempo muy corto. El simple gancho \hook{pretxncommit} de la +figura~\ref{ex:hook:msglen.go} evitará que usted consigne un conjunto +de cambios con un mensaje de menos de 10 bytes de longitud. + +\begin{figure}[ht] + \interaction{hook.msglen.go} + \caption{Un gancho que prohíbe mensajes de consignación demasiado + cortos} + \label{ex:hook:msglen.go} +\end{figure} + +\subsection{Comprobar espacios en blanco finales} + +Un uso interesante para ganchos relacionados con consignaciones es +ayudarle a escribir código más limpio. Un ejemplo simple de +%TODO dictum => regla +``código más limpio'' es la regla de que un cambio no debe añadir +líneas de texto que contengan ``espacios en blanco finales''. El +espacio en blanco final es una serie de caracteres de espacio y +tabulación que se encuentran al final de una línea de texto. En la +mayoría de los casos, el espacio en blanco final es innecesario, ruido +invisible, pero ocasionalmente es problemático, y la gente en general +prefiere deshacerse de él. + +Usted puede usar cualquiera de los ganchos \hook{precommit} o +\hook{pretxncommit} para revisar si tiene el problema de los espacios +en blanco finales. Si usa el gancho \hook{precommit}, el gancho no +sabrá qué ficheros se están consignando, por lo que se tendrá que +revisar cada fichero modificado en el repositorio para ver si tiene +espacios en blanco finales. Si usted sólo quiere consignar un cambio +al fichero \filename{foo}, y el fichero \filename{bar} contiene +espacios en blanco finales, hacer la revisión en el gancho +\hook{precommit} evitará que usted haga la consignación de +\filename{foo} debido al problem en \filename{bar}. Este no parece el +enfoque adeucado. + +Si usted escogiera el gancho \hook{pretxncommit}, la revisión no +ocurriría sino hasta justo antes de que la transacción para la +consignación se complete. Esto le permitirá comprobar por posibles +problemas sólo en los ficheros que serán consignados. Sin embargo, si +usted ingresó el mensaje de consignación de manera interactiva y el +%TODO roll-back +gancho falla, la transacción será deshecha; usted tendrá que +reingresar el mensaje de consignación luego de que corrija el problema +con los espacios en blanco finales y ejecute \hgcmd{commit} de nuevo. + +\begin{figure}[ht] + \interaction{hook.ws.simple} + \caption{Un gancho simple que revisa si hay espacios en blanco + finales} + \label{ex:hook:ws.simple} +\end{figure} + +La figura~\ref{ex:hook:ws.simple} presenta un gancho +\hook{pretxncommit} simple que comprueba la existencia de espacios en +blanco finales. Este gancho es corto, pero no brinda mucha ayuda. +Termina con un código de salida de error si un cambio añade una línea +con espacio en blanco final a cualquier fichero, pero no muestra +ninguna información que pueda ser útil para identificar el fichero o +la línea de texto origen del problema. También tiene la agradable +propiedad de no prestar atención a las líneas que no sufrieron +modificaciones; sólo las líneas que introducen nuevos espacios en +blanco finales causan problemas. + +\begin{figure}[ht] + \interaction{hook.ws.better} + \caption{Un mejor gancho para espacios en blanco finales} + \label{ex:hook:ws.better} +\end{figure} + +El ejemplo de la figura~\ref{ex:hook:ws.better} es mucho más complejo, +pero también más útil. El gancho procesa un diff unificado para +revisar si alguna línea añade espacios en blanco finales, e imprime el +nombre del fichero y el número de línea de cada ocurrencia. Aún mejor, +si el cambio añade espacios en blanco finales, este gancho guarda el +mensaje de consignación e imprime el nombre del fichero en el que el +mensaje fue guardado, antes de terminar e indicarle a Mercurial que +deshaga la transacción, para que uste pueda usar +\hgcmdargs{commit}{\hgopt{commit}{-l}~\emph{nombre\_fichero}} para +reutilizar el mensaje de consignación guardado anteriormente, una vez +usted haya corregido el problema. + +Como anotación final, note en la figura~\ref{ex:hook:ws.better} el +%TODO on-site => in-situ ? +uso de la característica de edición \emph{in-situ} de \command{perl} +para eliminar los espacios en blanco finales en un fichero. Esto es +lo suficientemente conciso y poderoso para que lo presente aquí. +% TODO corregí el backslash, y comprobé por mi cuenta un archivo +% aparte, y el comando hace lo que debe hacer. Favor copiar del pdf el +% comando perl y comprobar con un archivo con espacios en blanco +% finales, y si todo está bien (que debería), borrar esta nota +\begin{codesample2} + perl -pi -e 's,\textbackslash{}s+\$,,' nombre\_fichero +\end{codesample2} + +\section{Ganchos adicionales} + +Mercurial se instala con varios ganchos adicionales. Usted puede +encontrarlos en el directorio \dirname{hgext} del árbol de ficheros +fuente de Mercurial. Si usted está usando un paquete binario de +Mercurial, los ganchos estarán ubicados en el directorio +\dirname{hgext} en donde su instalador de paquetes haya puesto a +Mercurial. + +\subsection{\hgext{acl}---control de acceso a partes de un repositorio} + +La extensión \hgext{acl} le permite controlar a qué usuarios remotos +les está permitido empujar conjuntos de cambios a un servidor en red. +Usted puede proteger cualquier porción de un repositorio (incluyendo +el repositorio completo), de tal manera que un usuario remoto +específico pueda empujar cambios que no afecten la porción protegida. + +Esta extensión implementa control de acceso basado en la identidad del +usuario que empuja los conjuntos de cambios, \emph{no} en la identidad +de quien hizo la consignación de los mismos. Usar este gancho tiene +sentido sólo si se tiene un servidor adecuadamente asegurado que +autentique a los usuarios remotos, y si usted desea segurarse de que +sólo se le permita a ciertos usuarios empujar cambios a dicho +servidor. + +\subsubsection{Configuración del gancho \hook{acl}} + +Para administrar los conjuntos de cambios entrantes, se debe usar el +gancho \hgext{acl} como un gancho de tipo \hook{pretxnchangegroup}. +Esto le permite ver qué ficheros son modificados por cada conjunto de +%TODO rollback => "deshacer el efecto" +cambios entrante, y deshacer el efecto de un grupo de conjuntos de +cambios si alguno de ellos modifica algún fichero ``prohibido''. +Ejemplo: +\begin{codesample2} + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook +\end{codesample2} + +La extensión \hgext{acl} es configurada mediante tres secciones. + +La sección \rcsection{acl} sólo tiene una entrada, +\rcitem{acl}{sources}\ndt{Fuentes.}, que lista las fuentes de los +conjuntos de cambios entrantes a las que el gancho debe prestar +atención. Usualmente usted no necesita configurar esta sección. +\begin{itemize} + \item[\rcitem{acl}{serve}] Controlar conjuntos de + cambios entrantes que están llegando desde un repositorio a + través de http o ssh. Este es el valor por defecto de + \rcitem{acl}{sources}, y usualmente es el único valor de + configuración que necesitará para este ítem. +\item[\rcitem{acl}{pull}] Controlar conjuntos de cambios entrantes que + lleguen vía un pull (jalado) desde un repositorio local. +\item[\rcitem{acl}{push}] Controlar conjuntos de cambios entrantes que + lleguen vía un push (empuje) desde un repositorio local. +\item[\rcitem{acl}{bundle}] Controlar conjuntos de cambios entrantes + %TODO bundle + que lleguen desde otro repositorio a través de un paquete. +\end{itemize} + +La sección \rcsection{acl.allow} controla los usuarios a los que les +está permitido añadir conjuntos de cambios al repositorio. Si esta +sección no está presente, se le permite acceso a todos los usuarios +excepto a los que se les haya negado explícitamente el acceso. Si +esta sección no está presente, se niega el acceso a todos los usuarios +excepto a todos a los que se les haya permitido de manera explícita +(así que una sección vacía implica que se niega el acceso a todos los +usuarios). + +La sección \rcsection{acl.deny} determina a qué usuarios no se les +permite añadir conjuntos de cambios al repositorio. Si esta sección no +está presente o está vacía, no se niega el acceso a ningún usuario. + +La sintaxis para los ficheros \rcsection{acl.allow} y +\rcsection{acl.deny} es idéntica. A la izquierda de cada entrada se +encuentra un patrón glob que asocia ficheros o directorios, respecto a +la raíz del repositorio; a la derecha, un nombre usuario. + +En el siguiente ejemplo, el usuario \texttt{escritordoc} sólo puede +empujar cambios al directorio \dirname{docs} del repositorio, mientras +que \texttt{practicante} puede enviar cambios a cualquier fichero o +directorio excepto \dirname{fuentes/sensitivo}. +\begin{codesample2} + [acl.allow] + docs/** = escritordoc + + [acl.deny] + fuentes/sensitivo/** = practicante +\end{codesample2} + +\subsubsection{Pruebas y resolución de problemas} + +Si usted desea probar el gancho \hgext{acl}, ejecútelo habilitando la +opción de salida de depuración habilitada. Ya que usted probablemente +lo estará ejecutando en un servidor donde no es conveniente (o incluso +posible) pasar la opción \hggopt{--debug}, no olvide que usted puede +habilitar la salida de depuración en su \hgrc: +\begin{codesample2} + [ui] + debug = true +\end{codesample2} +Con esto habilitado, el gancho \hgext{acl} imprimirá suficiente +información para permitirle saber porqué está permitiendo o denegando +la operación de empujar a usuarios específicos. + +\subsection{\hgext{bugzilla}---integración con Bugzilla} + +La extensión \hgext{bugzilla} añade un comentario a un fallo Bugzilla +siempre que encuentre una referencia al ID de dicho fallo en un +mensaje de consignación. Usted puede instalar este gancho en un +servidor compartido, para que cada vez que un usuario remoto empuje +cambios al servidor, el gancho sea ejecutado. + +Se añade un comentario al fallo que se ve así (usted puede configurar +los contenidos del comentario---vea más abajo): +%TODO traducir? +\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} +El valor de este gancho se encuentra en que automatiza el proceso de +actualizar un fallo cuando un conjunto de cambios se refiera a él. Si +usted configura este gancho adecuadamente, hará fácil para la gente +navegar directamente desde un fallo Bugzilla a un conjunto de cambios +que se refiere a ese fallo. + +Usted puede usar el código de este gancho como un punto de partida +para otras recetas de integración con Bugzilla aún más exóticas. Acá +hay algunas posibilidades: +\begin{itemize} +\item Requerir que cada conjunto de cambios tenga un ID de fallo en su + mensaje de consignación. En este caso, usted querrá configurar el + gancho como uno de tipo \hook{pretxncommit}. Esto le permitirá al + gancho rechazar cambios que no contiene IDs de fallos. +\item Permitir a los conjuntos de cambios entrantes modificar + automáticamente el \emph{estado} de un fallo, así como simplemente + añadir un comentario. Por ejemplo, el gancho podría reconocer la + cadena ``corregido fallo 31337'' como la señal de que debería + actualizar el estado del fallo 31337 a ``requiere pruebas''. +\end{itemize} + +\subsubsection{Configuración del gancho \hook{bugzilla}} +\label{sec:hook:bugzilla:config} + +Usted debería configurar este gancho en el \hgrc\ de su servidor como +un gancho \hook{incoming}\ndt{Entrante.}, por ejemplo como sigue: +\begin{codesample2} + [hooks] + incoming.bugzilla = python:hgext.bugzilla.hook +\end{codesample2} + +Debido a la naturaleza especializada de este gancho, y porque Bugzilla +no fue escrito con este tipo de integración en mente, configurar este +% TODO involved => complejo ? no intarwebs here :( +gancho es un proceso algo complejo. + +Antes de empezar, usted debe instalar la interfaz de Python para MySQL +en los sistemas en los que se vaya a ejecutar el gancho. Si no está +disponible como paquete binario para su sistema, usted puede descargar +el paquete desde~\cite{web:mysql-python}. + +La información para configurar este gancho se ubica en la sección +\rcsection{bugzilla} de su \hgrc. +\begin{itemize} +\item[\rcitem{bugzilla}{version}] La versión de Bugzilla instalada en + el servidor. El esquema de base de datos que Bugzilla usa cambia + ocasionalmente, así que este gancho debe saber exactamente qué + esquema usar. A la fecha, la única versión soportada es la + \texttt{2.16}. +\item[\rcitem{bugzilla}{host}] El nombre de máquina (\emph{hostname}) + del servidor MySQL que almacena sus datos Bugzilla. La base de datos + debe ser configurada para permitir conexiones desde las máquinas en + las que usted ejecute el gancho \hook{bugzilla}. +\item[\rcitem{bugzilla}{user}] El nombre de usuario que se usará para + conectarse al servidor MySQL. La base de datos debe ser configurada + para permitir a dicho usuario conectarse desde cualquiera de las + máquinas en las que se ejecute el gancho \hook{bugzilla}. Este + usuario debe tener acceso y poder modificar las tablas de Bugzilla. + El valor por defecto para este ítem es \texttt{bugs}, que es el + nombre estándar del usuario para Bugzilla en una base de datos + MySQL. +\item[\rcitem{bugzilla}{password}] La contraseña MySQL para el usuario + configurado anteriormente. Ésta es almacenada como texto plano, así + que usted deberá asegurarse de que los usuarios no autorizados no + puedan leer el fichero \hgrc\ en donde usted guarda esta + información. +\item[\rcitem{bugzilla}{db}] El nombre de la base de datos Bugzilla en + el servidor MySQL. El nombre por defecto para este ítem es + \texttt{bugs}, que es el nombre estándar de la base de datos MySQL + en donde Bugzilla almacena sus datos. +\item[\rcitem{bugzilla}{notify}] Si usted desea que Bugzilla envíe un + %TODO suBscriptores? + correo de notificación a los suscriptores después de que el gancho + haya añadido un comentario a un fallo, necesitará que este gancho + ejecute un comando siempre que actualice la base de datos. El + comando que se ejecute depende de en dónde haya sido instalado + Bugzilla, pero típicamente se verá así, si usted ha instalado + Bugzilla en \dirname{/var/www/html/bugzilla}: + \begin{codesample4} + cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com + \end{codesample4} + El programa \texttt{processmail} de Bugzilla espera recibir un ID de + fallo (el gancho reemplaza ``\texttt{\%s}'' por el ID del fallo) y + una dirección de correo. También espera poder escribir a ciertos + ficheros en el directorio en que se ejecuta. Si Bugzilla y éste + gancho no están instalados en la misma máquina, usted deberá + encontrar una manera de ejecutar \texttt{processmail} en el servidor + donde está instalado Bugzilla. +\end{itemize} + +\subsubsection{Asociar nombres de consignadores a nombres de usuario +Bugzilla} + +Por defecto, el gancho \hgext{bugzilla} trata de usar la dirección de +correo electrónico de la persona que hizo la consignación del conjunto +de cambios como el nombre de usuario Bugzilla con el cual debe +actualizar el fallo. Si esto no se ajusta a sus necesidades, es +posible asociar direcciones de correo a nombres de usuario Bugzilla +usando una sección \rcsection{usermap}. + +Cada ítem en la sección \rcsection{usermap} contiene una dirección de +correo electrónico a la izquierda, y un nombre de usuario Bugzilla a +la derecha. +\begin{codesample2} + [usermap] + jane.user@example.com = jane +\end{codesample2} +Usted puede mantener los datos de \rcsection{usermap} en un fichero +\hgrc, o decirle al gancho \hgext{bugzilla} que lea la información +desde un fichero \filename{usermap} externo. En este caso, usted +puede almacenar los datos de \filename{usermap} en (por ejemplo) un +repositorio modificable por los usuarios. Esto hace posible para sus +usuarios mantener sus propias entradas \rcitem{bugzilla}{usermap}. El +fichero \hgrc\ principal se vería así: +\begin{codesample2} + # fichero hgrc normal se refiere a un fichero usermap externo + [bugzilla] + usermap = /home/hg/repos/userdata/bugzilla-usermap.conf +\end{codesample2} +Mientras que el fichero \filename{usermap} al que se hace referencia +se vería así: +\begin{codesample2} + # bugzilla-usermap.conf - dentro de un repositorio hg + [usermap] + stephanie@example.com = steph +\end{codesample2} + +\subsubsection{Configurar el texto que se añade a un fallo} + +Usted puede configurar el texto que este gancho añade como comentario; +usted los especifica como una plantilla Mercurial. Varias entradas +\hgrc\ (aún en la sección \rcsection{bugzilla}) controlan este +comportamiento. +\begin{itemize} +\item[\texttt{strip}] La cantidad de elementos iniciales de ruta a + remover de un nombre de ruta del repositorio para construir una ruta + parcial para una URL. Por ejemplo, si los repositorios en su + servidor se ubican en \dirname{/home/hg/repos}, y usted tiene un + repositorio cuya ruta es \dirname{/home/hg/repos/app/tests}, + entonces fijar \texttt{strip} a \texttt{4} resultará en una ruta + parcial de \dirname{app/tests}. El gancho hará disponible esta ruta + parcial cuando expanda una plantilla, como \texttt{webroot}. +\item[\texttt{template}] El texto de la plantilla a usar. En adición a + las variables usuales relacionadas con conjuntos de cambios, esta + plantilla puede usar \texttt{hgweb} (el valor del ítem de + configuración \texttt{hgweb} de arriba) y \texttt{webroot} (la ruta + construida usando \texttt{strip} arriba). +\end{itemize} + +Adicionalmente, usted puede añadir un ítem \rcitem{web}{baseurl} a la +sección \rcsection{web} de su \hgrc. El gancho \hgext{bugzilla} +publicará esto cuando expanda una plantilla, como la cadena base a +usar cuando se construya una URL que le permita a los usuarios navegar +desde un comentario de Bugzilla a la vista de un conjunto de cambios. +Ejemplo: +\begin{codesample2} + [web] + baseurl = http://hg.domain.com/ +\end{codesample2} + +A continuación se presenta un ejemplo completo de configuración para +el gancho \hgext{bugzilla}. +%TODO traducir? +\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{Pruebas y resolución de problemas} + +Los problemas más comunes que aparecen en la configuración del gancho +\hgext{bugzilla} suelen estar relacionados con la ejecución del guión +de Bugzilla \filename{processmail} y la asociación de nombres de +consignadores a nombres de usuario. + +Recuerde que en la sección~\ref{sec:hook:bugzilla:config} arriba el +usuario que ejecuta el proceso Mercurial en el servidor es también +el usuario que ejecutará el guión \filename{processmail}. El guión +\filename{processmail} algunas veces hace que Bugzilla escriba en +ficheros en su directorio de configuración, y los ficheros de +configuración de Bugzilla usualmente son propiedad del usuario bajo el +cual se ejecuta el servidor web. + +Usted puede hacer que \filename{processmail} sea ejecutado con la +identidad del usuario adecuado usando el comando \command{sudo}. A +continuación se presenta una entrada de ejemplo para un fichero +\filename{sudoers}. +\begin{codesample2} + hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s +\end{codesample2} +Esto permite que el usuario \texttt{hg\_user} ejecute el programa +\filename{processmail-wrapper} con la identidad del usuario +\texttt{httpd\_user}. + +Esta indirección a través de un guión envoltorio es necesaria, porque +\filename{processmail} espera que al ser ejecutado su directorio +actual sea aquel en el cual se instaló Bugzilla; usted no puede +especificar ese tipo de condición en un fichero \filename{sudoers}. +Los contenidos del giuón envoltorio son simples: +\begin{codesample2} + #!/bin/sh + cd `dirname $0` && ./processmail "$1" nobody@example.com +\end{codesample2} +No parece importar qué dirección de correo se le pase a +\filename{processmail}. + +Si su \rcsection{usermap} no es configurada correctamente, los +usuarios verán un mensaje de error del gancho \hgext{bugzilla} cuando +empujen cambios al servidor. El mensaje de error se verá así: +\begin{codesample2} + cannot find bugzilla user id for john.q.public@example.com +\end{codesample2} +Lo que esto quiere decir es que la dirección del consignador, +\texttt{john.q.public@example.com}, no es un nombre de usuario +Bugzilla válido, ni tiene una entrada en su \rcsection{usermap} que lo +asocie con un nombre de usuario válido Bugzilla. + +\subsection{\hgext{notify}---enviar notificaciones de correo +electrónico} + +%TODO feeds => notificaciones: lo más fácil es mirar en wikipedia +Aunque el servidor web embebido de Mercurial provee notificaciones de +cambios en cada repositorio, muchas personas prefieren recibir las +notificaciones de cambios vía correo electrónico. El gancho +\hgext{notify}\ndt{Notificación.} le permite a usted enviar +notificaciones a un conjunto de direcciones de correo cuando lleguen +conjuntos de cambios en los que los subscriptores estén interesados. + +De la misma forma que con el gancho \hgext{bugzilla}, el gancho +\hgext{notify} está orientado a plantillas, así que usted puede +personalizar los contenidos del mensaje de notificación que se envía. + +Por defecto, el gancho \hgext{notify} incluye un diff de cada conjunto +%TODO que se envía? revisar, pienso que es ``que se recibe'' +de cambios que se envía; usted puede limitar el tamaño del diff, o +desactivar completamente esta característica. Es útil para permitir a +los subscriptores revisar los cambios inmediatamente, en vez de tener +que hacer clic para visitar una URL. + +\subsubsection{Configuración del gancho \hgext{notify}} + +Usted puede configurar el gancho \hgext{notify} para enviar un mensaje +de correo por conjunto de cambios entrante, o uno por grupo entrante +de conjuntos de cambios (todos los que llegaron en un único empuje o +jalado). +\begin{codesample2} + [hooks] + # enviar un correo por grupo de cambios + changegroup.notify = python:hgext.notify.hook + # enviar un correo por cambio + incoming.notify = python:hgext.notify.hook +\end{codesample2} + +La información para configurar este gancho se ubica en la sección +\rcsection{notify} de un fichero \hgrc. +\begin{itemize} +\item[\rcitem{notify}{test}] Por defecto, este gancho no envía correos + en absoluto; en vez de eso, imprime el mensaje que se + \emph{enviaría}. Fije este ítem en \texttt{false} para permitir el + envío de correos. El motivo por el que el envío de correos está + desactivado es que hacen falta varios intentos para configurar esta + extensión exactamente como usted desea, y sería maleducado enviar a + los subscriptores una cantidad de notificaciones ``rotas'' mientras + usted depura su configuración. +\item[\rcitem{notify}{config}] La ruta a un fichero de configuración + que contiene información de subscripción. Esto se mantiene separado + del \hgrc\ principal para que usted pueda mantenerlo en un + repositorio. La gente puede clonar ese repositorio, actualizar sus + subscripciones, y empujar los cambios de vuelta a su servidor. +\item[\rcitem{notify}{strip}] La cantidad de caracteres iniciales de + separación de ruta a remover de la ruta del repositorio, al decidir + si un repositorio tiene subscriptores. Por ejemplo, si los + repositorios en su servidor están en \dirname{/home/hg/repos}, y + \hgext{notify} está trabajando con un repositorio llamado + \dirname{/home/hg/repos/shared/test}, fijar \rcitem{notify}{strip} a + \texttt{4} hará que \hgext{notify} elimine las partes iniciales de + la ruta hasta \dirname{shared/test}, y asociará los subscriptores + frente a dicha ruta. +\item[\rcitem{notify}{template}] El texto de plantilla a usar cuando + se envíen mensajes. Especifica los contenidos de la cabecera del + mensaje y el cuerpo del mismo. +\item[\rcitem{notify}{maxdiff}] El número máximo de líneas de datos de + diff a añadir al final de un mensaje. Si la longitud de un diff es + mayor a eso, se trunca. Por defecto, está fijado en 300. Fije esto a + \texttt{0} para omitir los diffs en los correos de notificación. +\item[\rcitem{notify}{sources}] Una lista de fuentes de conjuntos de + cambios a considerar. Esto le permite a usted indicar a + \hgext{notify} para que sólo envíe correos acerca de cambios que + usuarios remotos hayan empujado al repositorio vía un servidor, por + ejemplo. Vea la sección~\ref{sec:hook:sources} para las fuentes que + usted puede especificar aquí. +\end{itemize} + +Si usted fija el ítem \rcitem{web}{baseurl} en la sección +\rcsection{web}, usted lo puede usar en una plantilla; estará +disponible como \texttt{webroot}. + +A continuación se presenta un ejemplo completo de configuración para +el gancho \hgext{notify}. +\begin{codesample2} + [notify] + # enviar correo + test = false + # datos de subscriptores están en el repositorio notify + config = /home/hg/repos/notify/notify.conf + # repos están en /home/hg/repos on server, así que elimine 4 + # caracteres"/" + 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} + +Esto producirá un mensaje que se verá como el siguiente: +\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{Pruebas y resolución de problemas} + +No olvide que por defecto, la extensión \hgext{notify} \emph{no +enviará ningún correo electrónico} hasta que usted la configure +explícitamente para hacerlo, fijando el valor de \rcitem{notify}{test} +a \texttt{false}. Hasta que usted haga eso, simplemente se imprimirá +el mensaje que se \emph{enviaría}. + +\section{Información para escritores de ganchos} +\label{sec:hook:ref} + +\subsection{Ejecución de ganchos internos} + +Un gancho interno es llamado con argumentos de la siguiente forma: +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} +El parámetro \texttt{ui} es un objeto \pymodclass{mercurial.ui}{ui}. +El parámetro \texttt{repo} es un objeto +\pymodclass{mercurial.localrepo}{localrepository}. Los nombres y +valores de los parámetros en \texttt{**kwargs} dependen del gancho que +se invoque, con las siguientes características en común: +\begin{itemize} +\item Si hay un parámetro llamado \texttt{node} o + \texttt{parent\emph{N}}, contendrá un ID hexadecimal de un conjunto + de cambios. La cadena vacía es usada para representar un + ``ID de conjunto de cambios nulo'' en vez de una cadena de ceros. +\item Si hay un parámetro llamado \texttt{url}, contendrá la URL de un + repositorio remoto, si puede ser determinada. +\item Los parámetros booleanos son representados como objetos + \texttt{bool} de Python. +\end{itemize} + +Un gancho interno es ejecutado sin cambiar el directorio de trabajo +del proceso (a diferencia de los ganchos externos, que son ejecutados +desde la raíz del repositorio). El gancho no debe cambiar el +directorio de trabajo del proceso, porque esto haría que falle +cualquier llamada que se haga a la API de Mercurial. + +Si un gancho retorna el valor booleano ``false''\ndt{Falso.}, se +considera que éste tuvo éxito. Si retorna +``true''\ndt{Verdadero.} o genera una excepción, se considera que +ha fallado. Una manera útil de pensar en esta convención de llamado es +``dígame si usted falló''. + +Note que los IDs de conjuntos de cambios son pasados a los ganchos de +Python como cadenas hexadecimales, no como los hashes binarios que la +API de Mercurial usa normalmente. Para convertir un hash de +hexadecimal a binario, use la función \pymodfunc{mercurial.node}{bin}. + +\subsection{Ejecución de ganchos externos} + +Un gancho externo es pasado al intérprete de comandos del usuario bajo +el cual se ejecuta Mercurial. Las características del intérprete, como +sustitución de variables y redirección de comandos, están disponibles. +El gancho es ejecutado desde el directorio raíz del repositorio +(a diferencia de los ganchos internos, que se ejecutan desde el mismo +directorio en que Mercurial fue ejecutado). + +Los parámetros para el gancho se pasan como variables de entorno. El +nombre de cada variable de entorno se pasa a mayúsculas y se le añade +el prefijo ``\texttt{HG\_}''. Por ejemplo, si el nombre de un +parámetro es ``\texttt{node}'', el nombre de la variable de entorno +que almacena el parámetro se llamará ``\texttt{HG\_NODE}''. + +Un parámetro booleano se representa con la cadena ``\texttt{1}'' para +``true'', ``\texttt{0}'' para ``false''. Si una variable se llama +\envar{HG\_NODE}, \envar{HG\_PARENT1} o \envar{HG\_PARENT2}, +contendrá un ID de conjunto de cambios representado como una cadena +hexadecimal. La cadena vacía es usada para representar un ``ID de +conjunto de cambios nulo'' en vez de una cadena de ceros. Si una +variable de entorno se llama \envar{HG\_URL}, contendrá la URL de un +repositorio remoto, si puede ser determinada. + +Si un gancho termina con un código de salida de cero, se considera que +tuvo éxito. Si termina con un código de salida diferente de cero, se +considera que falló. + +\subsection{Averiguar de dónde vienen los conjuntos de cambios} +%TODO los trae la cigüeña. De París. Y quedan debajo de una col. + +Un gancho que involucra la transferencia de conjuntos de cambios entre +un repositorio local y otro puede ser capaz de averiguar información +acerca de ``el otro lado''. Mercurial sabe \emph{cómo} son +transferidos los conjuntos de cambios, y en muchos casos también desde +o hacia donde están siendo transferidos. + +\subsubsection{Fuentes de conjuntos de cambios} +\label{sec:hook:sources} + +Mercurial le indicará a un gancho cuáles son, o fueron, los medios +usados para transferir los conjuntos de cambios entre repositorios. +Esta información es provista por Mercurial en un parámetro Python +llamado \texttt{source}\ndt{Fuente.}, o una variable de entorno +llamada \envar{HG\_SOURCE}. + +\begin{itemize} +\item[\texttt{serve}] Los conjuntos de cambios son transferidos desde + o hacia un repositorio remoto a través de http o ssh. +\item[\texttt{pull}] Los conjuntos de cambios son transferidos vía una + operación de jalado de un repositorio a otro. +\item[\texttt{push}] Los conjuntos de cambios son transferidos vía un + empuje de un repositorio a otro. +\item[\texttt{bundle}] Los conjuntos de cambios son transferidos desde + %TODO bundle + o hacia un paquete. +\end{itemize} + +\subsubsection{A dónde van los cambios---URLs de repositorios remotos} +\label{sec:hook:url} +%TODO al cielo? no, ésos son los perros + +Cuando es posible, Mercurial le indicará a los ganchos la ubicación de +``el otro lado'' de una actividad que transfiera datos de conjuntos de +cambios entre repositorios. Esto es provisto por Mercurial en un +parámetro Python llamado \texttt{url}, o en una variable de entorno +llamada \envar{HG\_URL}. + +No siempre esta información está disponible. Si un gancho es invocado +un repositorio que es servido a través de http o ssh, Mercurial no +puede averiguar dónde está el repositorio remoto, pero puede saber +desde dónde se conecta el cliente. En esos casos, la URL tendrá una de +las siguientes formas: +\begin{itemize} +\item \texttt{remote:ssh:\emph{ip-address}}---cliente ssh remoto, en + la dirección IP dada. +\item \texttt{remote:http:\emph{ip-address}}---cliente remoto http, en + la dirección IP dada. Si el cliente está usando SSL, tendrá la forma + \texttt{remote:https:\emph{ip-address}}. +\item Vacío---no se pudo descubrir información acerca del cliente + remoto. +\end{itemize} + +\section{Referencia de ganchos} + +\subsection{\hook{changegroup}---luego de añadir conjuntos de cambios +remotos} +\label{sec:hook:changegroup} + +Este gancho es ejecutado luego de que un grupo de conjuntos de cambios +preexistentes ha sido añadido al repositorio, por ejemplo vía un +\hgcmd{pull} o \hgcmd{unbundle}. Este gancho es ejecutado una vez por +cada operación que añade uno o más conjuntos de cambios. Este gancho +se diferencia del gancho \hook{incoming}, que es ejecutado una vez por +cada conjunto de cambios, sin importar si los cambios llegan en grupo. + +Algunos usos posibles para este gancho includen el probar o ensamblar +los conjuntos de cambios añadidos, actualizar una base de datos de +fallos, o notificar a subscriptores de que el repositorio contiene +nuevos cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer conjunto + de cambios que fue añadido en el grupo. Todos los conjuntos de + cambios entre éste y la punta + %TODO mirar qué hacer con el índice + \index{tags!\texttt{tip}}(\texttt{tip}), inclusive, fueron añadidos + %TODO unbundle + por un único jalado (\hgcmd{pull}), empuje (\hgcmd{push}) o \hgcmd{unbundle}. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Veta también: \hook{incoming} (sección~\ref{sec:hook:incoming}), +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}), +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{commit}---luego de la creación de un nuevo conjunto +de cambios} +\label{sec:hook:commit} + +Este gancho es ejecutado luego de la creación de un nuevo conjunto de +cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID de conjunto + de cambios del conjunto de cambios que acabó de ser consignado. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del conjunto de cambios que + acaba de ser consignado. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del conjunto de cambios que + acaba de ser consignado. +\end{itemize} + +Vea también: \hook{precommit} (sección~\ref{sec:hook:precommit}), +\hook{pretxncommit} (sección~\ref{sec:hook:pretxncommit}) + +\subsection{\hook{incoming}---luego de que un conjunto de cambios +remoto es añadido} +\label{sec:hook:incoming} + +Este gancho es ejecutado luego de que un conjunto de cambios +preexistente ha sido añadido al repositorio, por ejemplo, vía un +\hgcmd{push}. Si un grupo de conjuntos de cambios fue añadido en una +sola operación, este gancho es ejecutado una vez por cada conjunto de +cambios añadido. + +Usted puede usar este gancho para los mismos fines que el gancho +\hook{changegroup} (sección~\ref{sec:hook:changegroup}); simplemente +algunas veces es más conveniente ejecutar un gancho una vez por cada +grupo de conjuntos de cambios, mientras que otras es más útil correrlo +por cada conjunto de cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del conjunto + de cambios recién añadido. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}) +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}), +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{outgoing}---luego de la propagación de los conjuntos +de cambios} +\label{sec:hook:outgoing} + +Este gancho es ejecutado luego de que un grupo de conjuntos de cambios +ha sido propagado fuera de éste repositorio, por ejemplo por un +comando \hgcmd{push} o \hgcmd{bundle}. + +Un uso posible para este gancho es notificar a los administradores que +los cambios han sido jalados. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer conjunto + de cambios del grupo que fue enviado. +\item[\texttt{source}] Una cadena. La fuente de la operación (vea la + sección~\ref{sec:hook:sources}). Si un cliente remoto jaló cambios + de este repositorio, \texttt{source} será \texttt{serve}. Si el + cliente que obtuvo los cambios desde este repositorio era local, + \texttt{source} será \texttt{bundle}, \texttt{pull}, o + \texttt{push}, dependiendo de la operación que llevó a cabo el + cliente. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{preoutgoing} (sección~\ref{sec:hook:preoutgoing}) + +\subsection{\hook{prechangegroup}---antes de empezar la adición de +conjuntos de cambios remotos} +\label{sec:hook:prechangegroup} + +Este gancho de control es ejecutado antes de que Mercurial empiece a +añadir un grupo de conjuntos de cambios de otro repositorio. + +Este gancho no tiene ninguna información acerca de los conjuntos de +cambios que van a ser añadidos, porque es ejecutado antes de que se +permita que empiece la transmisión de dichos conjuntos de cambios. Si +este gancho falla, los conjuntos de cambios no serán transmitidos. + +Un uso para este gancho es prevenir que se añadan cambios externos a un +repositorio. Por ejemplo, usted podría usarlo para ``congelar'' +temporal o permanentemente una rama ubicada en un servidor para que +los usuarios no puedan empujar cambios a ella, y permitiendo al mismo +tiempo modificaciones al repositorio por parte de un administrador +local. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}), +\hook{incoming} (sección~\ref{sec:hook:incoming}), , +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{precommit}---antes de iniciar la consignación de un +conjunto de cambios} +\label{sec:hook:precommit} + +Este gancho es ejecutado antes de que Mercurial inicie la consignación +de un nuevo conjunto de cambios. Es ejecutado antes de que Mercurial +tenga cualquier de los metadatos para la consignación, como los +ficheros a ser consignados, el mensaje de consignación, o la fecha de +consignación. + +Un uso para este gancho es deshabilitar la capacidad de consignar +nuevos conjuntos de cambios, pero permitiendo conjuntos de cambios +entrantes. Otro es ejecutar un proceso de ensamble/compilación o +prueba, y permitir la consignación sólo si el ensamble/compilación o +prueba tiene éxito. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del directorio de trabajo. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del directorio de trabajo. +\end{itemize} +Si la consignación continúa, los padres del directorio de trabajo se +convertirán en los padres del nuevo conjunto de cambios. + +Vea también: \hook{commit} (sección~\ref{sec:hook:commit}), +\hook{pretxncommit} (sección~\ref{sec:hook:pretxncommit}) + +\subsection{\hook{preoutgoing}---antes de empezar la propagación de +conjuntos de cambios} +\label{sec:hook:preoutgoing} + +Este gancho es ejecutado antes de que Mercurial conozca las +identidades de los conjuntos de cambios que deben ser transmitidos. + +Un uso para este gancho es evitar que los cambios sean transmitidos a +otro repositorio. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{source}] Una cadena. La fuente la operación que está + tratando de obtener cambios de éste repositorio (vea + la sección~\ref{sec:hook:sources}). Revise la documentación para + el parámetro \texttt{source} del gancho \hook{outgoing}, en la + sección~\ref{sec:hook:outgoing}, para ver los posibles valores de + este parámetro. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{outgoing} (sección~\ref{sec:hook:outgoing}) + +\subsection{\hook{pretag}---antes de etiquetar un conjunto de cambios} +\label{sec:hook:pretag} + +Este gancho de control es ejecutado antes de la creación de una +etiqueta. Si el gancho termina exitosamente, la creación de la +etiqueta continúa. Si el gancho falla, no se crea la etiqueta. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{local}] Un booleano. Indica si la etiqueta es local a + ésta instancia del repositorio (p.e.~almacenado en + \sfilename{.hg/localtags}) o administrado por Mercurial (almacenado + en \sfilename{.hgtags}). +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del conjunto + de cambios a etiquetar. +\item[\texttt{tag}] Una cadena. El nombre de la etiqueta por crear. +\end{itemize} + +Si la etiqueta que se va a crear se encuentra bajo control de +revisiones, los ganchos \hook{precommit} y \hook{pretxncommit} +(secciones~\ref{sec:hook:commit} y~\ref{sec:hook:pretxncommit}) +también serán ejecutados. + +Vea también: \hook{tag} (sección~\ref{sec:hook:tag}) + +\subsection{\hook{pretxnchangegroup}---antes de completar la adición +de conjuntos de cambios remotos} +\label{sec:hook:pretxnchangegroup} + +Este gancho de control es ejecutado antes de una transacción---la que +maneja la adición de un grupo de conjuntos de cambios nuevos desde +fuera del repositorio---se complete. Si el gancho tiene éxito, la +transacción se completa, y todos los conjuntos de cambios se vuelven +permanentes dentro de este repositorio. Si el gancho falla, la +transacción es deshecha, y los datos para los conjuntos de cambios son +eliminados. + +Este gancho puede acceder a los metadatos asociados con los conjuntos +de cambios casi añadidos, pero no debe hacer nada permanente con estos +datos. Tampoco debe modificar el directorio de trabajo. + +Mientras este gancho está corriendo, si otro proceso Mercurial accesa +el repositorio, podrá ver los conjuntos de cambios casi añadidos como +si fueran permanentes. Esto puede llevar a condiciones de carrera si +usted no toma precauciones para evitarlas. + +Este gancho puede ser usado para examinar automáticamente un grupo de +conjuntos de cambios. Si el gancho falla, todos los conjuntos de +cambios son ``rechazados'' cuando la transacción se deshace. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer + conjunto de cambios que fue añadido en el grupo. Todos los + conjuntos de cambios entre éste y el + \index{tags!\texttt{tip}}\texttt{tip}, inclusive, fueron añadidos + por un único \hgcmd{pull}, \hgcmd{push} o \hgcmd{unbundle}. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}), +\hook{incoming} (sección~\ref{sec:hook:incoming}), +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}) + +\subsection{\hook{pretxncommit}---antes de completar la consignación +de un nuevo conjunto de cambios} +\label{sec:hook:pretxncommit} + +Este gancho de control es ejecutado antes de que una transacción---que +maneja una nueva consignación---se complete. Si el gancho tiene éxito, +la transacción se completa y el conjunto de cambios se hace permanente +dentro de éste repositorio. Si el gancho falla, la transacción es +deshecha, y los datos de consignación son borrados. + +Este gancho tiene acceso a los metadatos asociados con el +prácticamente nuevo conjunto de cambios, pero no debería hacer nada +permanente con estos datos. Tampoco debe modificar el directorio de +trabajo. + +Mientras este gancho está corriendo, si otro proceso Mercurial accesa +éste repositorio, podrá ver el prácticamente nuevo conjunto de cambios +como si fuera permanente. Esto puede llevar a condiciones de carrera si +usted no toma precauciones para evitarlas. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{node}] Un ID de conjunto de cambios. El ID del + conjunto de cambios recién consignado. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del conjunto de cambios que + acaba de ser consignado. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del conjunto de cambios que + acaba de ser consignado. +\end{itemize} + +Vea también: \hook{precommit} (sección~\ref{sec:hook:precommit}) + +\subsection{\hook{preupdate}---antes de actualizar o fusionar el +directorio de trabajo} +\label{sec:hook:preupdate} + +Este gancho de control es ejecutado antes de actualizar o fusionar el +directorio de trabajo. Es ejecutado sólo si las revisiones usuales de +Mercurial antes de las actualizaciones determinan que la actualización +o fusión pueden proceder. Si el gancho termina exitosamente, la +actualización o fusión pueden proceder.; si falla, la actualización o +fusión no empiezan. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID del + padre al que el directorio de trabajo será actualizado. Si se está + fusionando el directorio de trabajo, no cambiará este padre. +\item[\texttt{parent2}] Un ID de conjunto de cambios. Sólo está + definido si se está fusionando el directorio de trabajo. El ID de la + revisión con la cual está siendo fusionado el directorio de trabajo. +\end{itemize} + +Vea también: \hook{update} (sección~\ref{sec:hook:update}) + +\subsection{\hook{tag}---luego de etiquetar un conjunto de cambios} +\label{sec:hook:tag} + +Este gancho es ejecutado luego de la creación de una etiqueta. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{local}] Un booleano. Indica si la etiqueta es local a + ésta instancia del repositorio (p.e.~almacenado en + \sfilename{.hg/localtags}) o administrado por Mercurial (almacenado + en \sfilename{.hgtags}). +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del + conjunto de cambios que fue etiquetado. +\item[\texttt{tag}] Una cadena. El nombre de la etiqueta que fue + creada. +\end{itemize} + +Si la etiqueta creada está bajo control de revisiones, el gancho +\hook{commit} (sección~\ref{sec:hook:commit}) es ejecutado antes de +este gancho. + +Vea también: \hook{pretag} (sección~\ref{sec:hook:pretag}) + +\subsection{\hook{update}---luego de actualizar o fusionar el +directorio de trabajo} +\label{sec:hook:update} + +Este gancho es ejecutado después de una actualización o fusión en el +directorio de trabajo. Ya que una fusión puede fallar (si el comando +externo \command{hgmerge} no puede resolver los conflictos en un +fichero), este gancho indica si la actualización o fusión fueron +completados adecuadamente. + +\begin{itemize} +\item[\texttt{error}] Un booleano. Indica si la actualización o fusión + fue completada exitosamente. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID del padre + al cual fue actualizado el directorio de trabajo. Si se fusionó el + directorio de trabajo, no se habrá cambiado este padre. +\item[\texttt{parent2}] Un ID de conjunto de cambios. Sólo está + definido si se fusionó el directorio de trabajo. El ID de la + revisión con la que fue fusionado el directorio de trabajo. +\end{itemize} + +Vea también: \hook{preupdate} (sección~\ref{sec:hook:preupdate}) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/htlatex.book --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/htlatex.book Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +../en/htlatex.book \ No newline at end of file diff -r 5981a0f7540a -r 019040fbf5f5 es/intro.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/intro.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,621 @@ +\chapter{Introducción} +\label{chap:intro} + +\section{Acerca del control de revisiones} + +El control de revisiones es el proceso de administrar diferentes +versiones de una pieza de información. En su forma más simple es algo +que la mayoría de gente hace a mano: cada vez que usted modifica un +fichero, lo graba con un nuevo nombre que contiene un número, cada uno +mayor que el anterior. + +Administrar manualmente muchas versiones de incluso sólo un fichero es una tarea +propensa a errores, a pesar de que hace bastante tiempo hay +herramientas que ayudan en este proceso. Las primeras herramientas +para automatizar el control de revisiones fueron pensadas para que un +usuario administrara un solo fichero. En las décadas pasadas, el +alcance de las herramientas de control de revisiones ha ido aumentando +considerablemente; ahora manejan muchos ficheros y facilitan el +trabajo en conjunto de varias personas. Las mejores herramientas de +control de revisiones de la actualidad no tienen problema con miles de +personas trabajando en proyectos que consisten de cientos de miles de +ficheros. + +\subsection{¿Por qué usar control de revisiones?} + +Hay muchas razones por las cuales usted o su equipo desearía usar una +herramienta automática de control de revisiones para un proyecto. +\begin{itemize} +\item Llevar registro del historial y la evolución de su proyecto, para + evitar hacer la tarea manualmente. Por cada cambio, tendrá una + bitácora de \emph{quién} lo hizo; \emph{por qué} se hizo; + \emph{cuándo} se hizo; y de \emph{qué} se trataba el cambio. +\item Cuando trabaja con más personas, los programas de control de + revisiones facilitan la colaboración. Por ejemplo, cuando varias + personas hacen cambios potencialmente incompatibles de forma casi + simultánea, el programa le ayudará a identificar y resolver tales + conflictos. +\item Puede ayudarle a recuperarse de equivocaciones. Si aplica un + cambio que posteriormente se evidencia como un error, puede + revertirlo a una versión previa a uno o muchos ficheros. De hecho, + una herramienta \emph{realmente} buena, incluso puede ayudarle + efectivamente a darse cuenta exactamente cuándo se introdujo el + error (para más detalles ver la sección~\ref{sec:undo:bisect}). +\item Le ayudará a trabajar simultáneamente, y a manejar las diferencias + entre múltiples versiones de su proyecto. +\end{itemize} +La mayoría de estas razones son igualmente válidas ---por lo menos en +teoría--- así esté trabajando en un proyecto solo usted, o con mucha gente. + +Algo fundamental acerca de lo práctico de un sistema de control de +revisiones en estas dos escalas (``un hacker solitario'' y ``un equipo +gigantesco'') es cómo se comparan los \emph{beneficios} con los +\emph{costos}. Una herramienta de control de revisiones que sea +difícil de entender o usar impondrá un costo alto. + +Un proyecto de quinientas personas es muy propenso a colapsar +solamente con su peso inmediatamente sin una herramienta y un proceso +de control de versiones. En este caso, el costo de usar control de +revisiones ni siquiera se tiene en cuenta, puesto que \emph{sin} él, +el fracaso está casi garantizado. + +Por otra parte, un ``arreglo rápido'' de una sola persona, excluiría +la necesidad de usar una herramienta de control de revisiones, porque +casi seguramente, el costo de usar una estaría cerca del costo del +proyecto. ¿No es así? + +Mercurial soporta \emph{ambas} escalas de de desarrollo de manera +única. Puede aprender lo básico en pocos minutos, y dado su bajo +sobrecosto, puede aplicar el control de revisiones al proyecto más +pequeño con facilidad. Su simplicidad significa que no tendrá que +preocuparse por conceptos obtusos o secuencias de órdenes compitiendo +por espacio mental con lo que sea que \emph{realmente} esté tratando +de hacer. Al mismo tiempo, Mercurial tiene alto desempeño y su +%TODO distribuida? en vez de p2p +naturaleza peer-to-peer le permite escalar indoloramente para manejar +grandes proyectos. + +Ninguna herramienta de control de revisiones puede salvar un +proyecto mal administrado, pero la elección de herramientas puede +hacer una gran diferencia en la fluidez con la cual usted puede +trabajar en un proyecto. + +\subsection{La cantidad de nombres del control de revisiones} + +El control de revisiones es un campo amplio, tan amplio que no hay un +acrónimo o nombre único. A continuación presentamos un listado de +nombres comunes y acrónimos que se podrían encontrar: +\begin{itemize} +\item Control de revisiones (RCS) +\item Manejo de Configuraciones de Programas (SCM), o administracón de + configuraciones +\item Administración de código fuente +\item Control de Código Fuente, o Control de Fuentes +\item Control de Versiones (VCS) +\end{itemize} +Algunas personas aducen que estos términos tienen significados +diversos, pero en la práctica se sobreponen tanto que no hay una +forma acordada o incluso adecuada de separarlos. + +\section{Historia resumida del control de revisiones} + +La herramienta de control de revisiones más antigua conocida es SCCS +(Sistema de Control de Código), escrito por Marc Rochkind en Bell +Labs, a comienzos de los setentas (1970s). SCCS operaba sobre ficheros +individuales, y requería que cada persona que trabajara en el proyecto +tuviera acceso a un espacio compartido en un solo sistema. Solamente +una persona podía modificar un fichero en un momento dado; el +arbitramiento del acceso a los ficheros se hacía con candados. Era +común que la gente pusiera los candados a los ficheros, y que +posteriormente olvidara quitarlos, impidiendo que otro pudiera +modificar los ficheros en cuestión sin la intervención del +administrador. + +Walter Tichy desarrolló una alternativa gratuita a SCCS a comienzos +de los ochentas (1980s); llamó a su programa RCS (Sistema de Control de +Revisiones). Al igual que SCCS, RCS requería que los desarrolladores +trabajaran en un único espacio compartido y colocaran candados a los +ficheros para evitar que varias personas los modificaran +simultáneamente. + +Después en los ochenta, Dick Grune usó RCS como un bloque de +construcción para un conjunto de guiones de línea de comando, que +inicialmente llamó cmt, pero que renombró a CVS (Sistema Concurrente de +Versiones). La gran innovación de CVS era que permitía a los +desarrolladores trabajar simultáneamente de una forma más o menos +independiente en sus propios espacios de trabajo. Los espacios de +trabajo personales impedían que los desarrolladores se pisaran las +mangueras todo el tiempo, situación común con SCCS y RCS. Cada +desarrollador tenía una copia de todos los ficheros del proyecto y podía +modificar sus copias independientemente, Tenían que fusionar sus +ediciones antes de consignar los cambios al repositorio central. + +Brian Berliner tomó los scripts originales de Grune y los reescribió +en~C, publicando en 1989 el código sobre el cual se ha +desarrollado la versión moderna de CVS. CVS adquirió posteriormente +la habilidad de operar sobre una conexión de red, dotándolo de una +arquitectura, cliente/servidor. La arquitectura de CVS es +centralizada; el historial del proyecto está únicamente en el +repositorio central. Los espacios de trabajo de los clientes +contienen únicamente copias recientes de las versiones de los +ficheros, y pocos metadatos para indicar dónde está el servidor. CVS +ha tenido un éxito enorme; Es probablemente el sistema de control de +revisiones más extendido del planeta. + +A comienzos de los noventa~(1990s), Sun MicroSystems desarrollo un +temprano sistema distribuido de control de revisiones llamado +TeamWare. +Un espacio de trabajo TeamWare contiene una copia completa del +historial del proyecto. TeamWare no tiene la noción de repositorio +central. (CVS se basaba en RCS para el almacenamiento de su historial; +TeamWare usaba SCCS.) + +A medida que avanzaba la decada de los noventa, se empezó a +evidenciar los problemas de CVS. Almacena cambios simultáneos a muchos +ficheros de forma individual, en lugar de agruparlos como una +operación única y atómica lógicamente. No maneja bien su jerarquía de +ficheros; es fácil desordenar un repositorio al renombrar ficheros +y directorios. Peor aún, su código fuente es difícil de leer y +mantener, lo que hizo que su ``umbral de dolor'' para arreglar sus +problemas arquitecturales fuera algo prohibitivo. + +En 2001, Jim Blandy y Karl Fogel, dos desarrolladores que habían +trabajado en CVS, comenzaron un proyecto para reemplazarlo con una +herramienta con mejor arquitectura y código más limpio. El resultado, +Subversion, no se separó del modelo centralizado cliente/servidor de +CVS, pero añadió consignaciones atómicas de varios ficheros, mejor +manejo de espacios de nombres , y otras características que lo hacen +mejor que CVS. Desde su versión inicial, ha ido creciendo en +popularidad rápidamente. + +Más o menos en forma simultánea Graydon Hoare comenzó a trabajar en un +ambicioso sistema distribuido de control de versiones que llamó +Monotone. Mientras que Monotone se enfocaba a evitar algunas fallas de +diseño de CVS con una arquitectura peer-to-peer, fue mucho más +allá de las herramientas anteriores (y posteriores) de +control de revisiones en varios aspectos innovadores. Usa hashes +criptográficos como identificadores, y tiene una noción integral de +``confianza'' para código de diversas fuentes. + +Mercurial nació en el 2005. Algunos de sus aspectos de de diseño +fueron influenciados por Monotone, pero Mercurial se enfoca en la +facilidad de uso, gran rendimiento y escalabilidad para proyectos muy +grandes. + +\section{Tendencias en el control de revisiones} + +Ha habido una tendencia inconfundible en el desarrollo y uso de las herramientas +de control de revisiones en las cuatro décadas pasadas, mientras la +gente se ha hecho familiar con las capacidades de sus herramientas y +se ha visto restringida por sus limitaciones. + +La primera generación comenzó administrando ficheros individuales en +computadores por persona. A pesar de que tales herramientas +representaron un avance importante frente al control de revisiones +manual, su modelo de candados y la dependencia a un sólo computador +los limitó a equipos de trabajo pequeños y acoplados. + +La segunda generación dejó atrás esas limitaciones moviéndose a +arquitecturas centradas en redes, y administrando proyectos completos +a la vez. A medida que los proyectos crecían, nacieron nuevos +problemas. Con la necesidad de comunicación frecuente con los +servidores, escalar estas máquinas se convirtió en un problema en +proyectos realmente grandes. Las redes con poca estabilidad podrían +impedir que usuarios remotos se conectaran al servidor. A medida que +los +proyectos de código abierto comenzaron a ofrecer acceso de sólo lectura +de forma anónima a cualquiera, la gente sin permiso para consignar +vio que no podían usar tales herramientas para interactuar en un +proyecto de forma natural, puesto que no podían guardar sus cambios. + +La generación actual de herramientas de control de revisiones es +peer-to-peer por naturaleza. Todos estos sistemas han eliminado la +dependencia de un único servidor central, y han permitido que la +gente distribuya sus datos de control de revisiones donde realmente se +necesita. La colaboración a través de Internet ha cambiado las +limitantes tecnológicas por la cuestión de elección y consenso. Las +herramientas modernas pueden operar sin conexión indefinidamente y +autónomamente, necesitando una conexión de red solamente para +sincronizar los cambios con otro repositorio. + +\section{Algunas ventajas del control distribuido de revisiones} + +A pesar de que las herramientas para el control distribuido de +revisiones lleva varios años siendo tan robustas y usables como la +generación previa de sus contrapartes, algunas personas que usan las +herramientas más antiguas no se han percatado de sus ventajas. Hay +gran cantidad +de situaciones en las cuales las herramientas distribuidas brillan +frente a las centralizadas. + +Para un desarrollador individual, las herramientas distribuidas casi +siempre son más rápidas que las centralizadas. Por una razón sencilla: +Una herramienta centralizada necesita comunicarse por red para las +operaciones más usuales, debido a que los metadatos se almacenan en +una sola copia en el servidor central. Una herramienta distribuida +almacena todos sus metadatos localmente. Con todo lo demás de la +misma forma, comunicarse por red tiene un sobrecosto en una +herramienta centralizada. No subestime el valor de una herramienta de +respuesta rápida: Usted empleará mucho tiempo interactuando con su +programa de control de revisiones. + +Las herramientas distribuidas son indiferentes a los caprichos de su +infraestructura de servidores, de nuevo, debido a la replicación de +metadatos en tantos lugares. Si usa un sistema centralizado y su +servidor explota, ojalá los medios físicos de su copia de seguridad +sean confiables, y que su última copia sea reciente y además +funcione. Con una herramienta distribuida tiene tantas copias de +seguridad disponibles como computadores de contribuidores. + +La confiabilidad de su red afectará las herramientas distribuidas de +una forma mucho menor que a las herramientas centralizadas. Usted no puede +siquiera usar una herramienta centralizada sin conexión de red, +excepto por algunas órdenes muy limitadas. Con herramientas +distribuidas, si sus conexión cae mientras usted está trabajando, +podría nisiquiera darse cuenta. Lo único que que no podrá hacer es +comunicarse con repositorios en otros computadores, algo que es +relativamente raro comparado con las operaciones locales. Si tiene +colaboradores remotos en su equipo, puede ser importante. + +\subsection{Ventajas para proyectos de código abierto} + +Si descubre un proyecto de código abierto y decide que desea comenzar +a trabajar en él, y ese proyecto usa una herramienta de control +distribuido de revisiones, usted es de inmediato un par con la gente que se +considera el ``alma'' del proyecto. Si ellos publican sus +repositorios, usted puede copiar inmediatamente el historial del proyecto, +hacer cambios y guardar su trabajo, usando las mismas herramientas de +la misma forma que ellos. En contraste, con una herramienta +centralizada, usted debe usar el programa en un modo ``sólo lectura'' a +menos que alguien le otorgue permisos para consignar cambios en el +repositorio central. Hasta entonces, no podrá almacenar sus cambios y +sus modificaciones locales correrán el riesgo de dañarse cuando trate +de actualizar su vista del repositorio. + +\subsubsection{Las bifurcaciones (forks) no son un problema} + +Se ha mencionado que las herramientas de control distribuido de +versiones albergan un riesgo a los proyectos de código abierto, puesto +que se vuelve muy sencillo hacer una ``bifurcación''\ndt{fork.} del +desarrollo del proyecto. Una bifurcación sucede cuando hay diferencias +de opinión o actitud entre grupos de desarrolladores que desemboca en +la decisión de la imposibilidad de continuar trabajando juntos. Cada +parte toma una copia más o menos completa del código fuente del +proyecto y toma su propio rumbo. + +En algunas ocasiones los líderes de las bifurcaciones reconcilian sus +diferencias. Con un sistema centralizado de control de revisiones, el +proceso \emph{técnico} de reconciliarse es doloroso, y se hace de +forma muy manual. Usted tiene que decidir qué historial de revisiones va a +``ganar'', e injertar los cambios del otro equipo en el árbol de alguna +manera. Con esto usualmente se pierde algo o todo del historial de la +revisión de alguna de las partes. + +Lo que las herramientas distribuidas hacen con respecto a las +bifurcaciones, es que las bifurcaciones son la \emph{única} forma de +desarrollar un proyecto. Cada cambio que usted hace es potencialmente +un punto de bifurcación. La gran fortaleza de esta aproximación es que +las herramientas distribuidas de control de revisiones tiene que ser +bueno al \emph{fusionar} las bifurcaciones, porque las bifurcaciones +son absolutamente fundamentales: pasan todo el tiempo. + +Si todas las porciones de trabajo que todos hacen, todo el tiempo, se +enmarcan en términos de bifurcaciones y fusiones, entonces a aquello a +lo que se refiere en el mundo del código abierto a una ``bifurcación'' +se convierte \emph{puramente} en una cuestión social. Lo que hacen las +herramientas distribuidas es \emph{disminuir} la posibilidad de una +bifurcación porque: +\begin{itemize} +\item Eliminan la distinción social que imponen las herramientas + centralizadas: aquélla entre miembros (personas con permiso de + consignar) y forasteros (los que no tienen el permiso). +\item Facilitan la reconciliación después de una bifurcación social, + porque todo lo que concierne al programa de control de revisiones es + una fusión. +\end{itemize} + +Algunas personas se resisten a las herramientas distribuidas porque +desean mantener control completo sobre sus proyectos, y creen que las +herramientas centralizadas les dan tal control. En todo caso, si este +es su parecer, y usted publica sus repositorios de CVS o Subversion, hay +muchas herramientas disponibles que pueden obtener el historial +completo (aunque sea lentamente) y recrearlo en otro sitio que usted no +controla. Siendo así un control ilusorio, puesto que está impidiendo +la fluidez de colaboración en lugar de prevenir que alguien se sienta +impulsado a obtener una copia y hacer una bifurcación con su historial. + +\subsection{Ventajas para proyectos comerciales} + +Muchos proyectos comerciales tienen grupos de trabajo distribuidos +alrededor del globo. Quienes contribuyen y están lejos de un +repositorio central verán una ejecución más lenta de los comandos y tal +vez menos confiabilidad. Los sistemas de control de revisión +comerciales intentan amortiguar estos problemas con adiciones de +replicación remota que usualmente son muy costosos y complicados de +administrar. Un sistema distribuido no padece estos problemas. Mejor +aún, puede colocar varios servidores autorizados, por ejemplo, uno por +sitio, de tal forma que no haya comunicación redundante entre +repositorios sobre enlaces de conexión costosos. + +Los sistemas de control de revisiones distribuidos tienden a ser poco +escalables. No es inusual que costosos sistemas centralizados caigan +ante la carga combinada de unas cuantas docenas de usuarios +concurrentes. De nuevo, las respuestas típicas de replicación tienden +a ser costosas y complejas de instalar y administrar. Dado que la +carga en un servidor central---si es que tiene uno---es muchas veces +menor con una herramienta distribuida (debido a que los datos están +replicados en todas partes), un solo servidor económico puede tratar +las necesidades de equipos mucho más grandes, y la replicación para +balancear la carga se vuelve cosa de guiones. + +Si tiene un empleado en el campo, se beneficiará grandemente de un +sistema distribuido de control de versiones al resolver problemas en +el sitio del cliente. La herramienta le permitirá generar +construcciones a la medida, probar diferentes arreglos de forma +independiente y buscar de forma eficiente las fuentes de fallos en el +historial y regresiones en los ambientes de los clientes, todo sin +necesidad de conectarse al servidor de su compañía. + +\section{¿Por qué elegir Mercurial?} + +Mercurial cuenta con un conjunto único de propiedades que lo hacen +una elección particularmente buena como sistema de control de +revisiones, puesto que: +\begin{itemize} +\item Es fácil de aprender y usar. +\item Es liviano. +\item Escala de forma excelente. +\item Es fácil de acondicionar. +\end{itemize} + +Si los sistemas de control de revisiones le son familiares, debería +estar listo para usar Mercurial en menos de cinco minutos. Si no, sólo va a +tomar unos pocos minutos más. Las órdenes de Mercurial y su conjunto +de características son uniformes y consistentes generalmente, y basta +con que siga unas pocas reglas generales en lugar de un montón de +excepciones. + +En un proyecto pequeño, usted puede comenzar a trabajar con Mercurial en +pocos momentos. Crear nuevos cambios y ramas, transferir cambios (localmente +o por la red); y las operaciones relacionadas con el estado y el +historial son rápidas. Mercurial buscar ser ligero y no incomodar en su +camino combinando poca sobrecarga cognitiva con operaciones +asombrosamente rápidas. + +La utilidad de Mercurial no se limita a proyectos pequeños: está +siendo usado por proyectos con centenas de miles de contribuyentes, +cada uno conteniendo decenas de miles de ficheros y centenas de +megabytes de código fuente + +Si la funcionalidad básica de Mercurial no es suficiente para usted, +es muy fácil extenderlo. Mercurial se comporta muy bien con tareas de +scripting y su limpieza interna junto con su implementación en Python +permiten añadir características fácilmente en forma de extensiones. +Hay un buen número de extensiones útiles y populares en este momento, +desde ayudar a identificar fallos hasta mejorar su desempeño. + +\section{Comparación de Mercurial con otras herramientas} + +Antes de leer, por favor tenga en cuenta que esta sección +necesariamente refleja mis propias experiencias, intereses y (tengo que +decirlo) mis preferencias. He usado cada una de las herramientas de +control de versiones listadas a continuación, y en muchos casos por +varios años. + + +\subsection{Subversion} + +Subversion es una herramienta de control de revisiones muy popular, +desarrollada para reemplazar a CVS. Tiene una arquitectura +centralizada tipo cliente/servidor. + +Subversion y Mercurial tienen comandos con nombres similares para hacer +las mismas operaciones, por lo que si le son familiares en una, será +sencillo aprender a usar la otra. Ambas herramientas son portables en +todos los sistemas operativos populares. + +Antes de la versión 1.5, Subversion no tenía soporte para fusiones. En +el momento de la escritura, sus capcidades para llevar cuenta de las +funciones son nuevas, +\href{http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword}{complicadas + y poco estables\ndt{buggy}}. + +Mercurial tiene una ventaja considerable en desempeño sobre +Subversion en cualquier operación de control de revisiones que yo haya +medido. He medido sus ventajas con factores desde dos hasta seis veces +comparando con almacenamiento de ficheros \emph{ra\_local} +Subversion~1.4.3, el cual es el método de acceso más rápido. En los +escenarios más realistas incluyendo almacenamiento con la red de por +medio, Subversion se encuentra en desventaja aún mayor. Dado que casi +todas las órdenes de Subversion deben tratar con el servidor y +Subversion no tiene utilidades de replicación adecuadas, la capacidad +del servidor y el ancho de banda se convierten en cuellos de botella +para proyectos modestamente grandes. + +Adicionalmente, Subversion tiene un sobrecosto considerable en +almacenamiento para evitar transacciones por red en algunas +operaciones, +tales como encontrar ficheros modificados (\texttt{status}) y desplegar +información frente a la revisión actual (\texttt{diff}). Como +resultado, la copia de trabajo de Subversion termina siendo del mismo +tamaño o más grande que un repositorio de Mercurial y el directorio de +trabajo, a pesar de que el repositorio de Mercurial contiene el +historial completo del proyecto. + +Subversion tiene soporte amplio de otras herramientas. Mercurial por +ahora está bastante atrás en este aspecto. Esta diferencia está +disminuyendo, y algunas de las herramientas GUI\ndt{Interfaz de + Usuario Gráfica}, eclipsan sus equivalentes de Subversion. Al igual +que Mercurial, Subversion tiene un excelente manual de usuario. + +Dado que Subversion no almacena el historial de revisiones en el +cliente, es muy bueno para administrar proyectos que tienen muchos +ficheros binarios grandes y opacos. Si consigna cincuenta revisiones +de un fichero de 10MB que no es comprimible, el esapacio en el cliente +de Subversion se mantendrá constante mientras que el espacio usado por +cualquier Sistema Distribuido de Control de Revisiones crecerá +rápidamente en proporción con el número de revisiones, debido a que +las diferencias entre cada revisión es grande. + +Adicionalmente, generalmente es difícil o más bien, imposible mezclar +diferentes versiones de un fichero binario. La habilidad de Subversion +para permitirle al usuario poner una cerradura a un fichero, de modo +que tenga un permiso exclusivo para consignar cambios, puede ser una +ventaja significativa en un proyecto donde los ficheros binarios sean +usados ampliamente. + +Mercurial puede importar el historial de revisiones de un repositorio +de Subversion. También puede exportar el historial de revisiones a un +repositorio de Subversion. De esta forma es sencillo ``dar un +vistazo'' y usar Mercurial y Subversion en paralelo antes de decidirse +a dar el paso. La conversión del historial es incremental, de modo +que puede aplicar una conversión inicial, y después conversiones +pequeñas y adicionales posteriormente para traer nuevos cambios. + +\subsection{Git} + +Git es una herramienta distribuida de control de revisiones +desarrollada para administrar el arbol del kernel de Linux. Al igual +que Mercurial los principios de su diseño fueron influenciados por +Monotone. + +Git tiene un conjunto de órdenes muy grande; en la versión~1.5.0 +ofrece~139 órdenes individuales. Tiene cierta reputación de ser +difícil de aprender. Comparado con Git, Mercurial tiene un fuerte +enfoque hacia la facilidad. + +En términos de rendimiento, Git es extremadamente rápido. En muchos +casos, es más rápido que Mercurial, por lo menos en Linux, mientras +que Mercurial se comporta mejor en otras operaciones. De todas +maneras en Windows, el desempeño y el nivel general de soporte que +ofrece Git, al momento de la escritura, está bastante atrás de +Mercurial. + +Mientras que el repositorio de Mercurial no requiere mantenimiento, el +repositorio de Git requiere frecuentes ``reempaquetados'' de sus metadatos. +Sin estos, el desempeño se degrada y el uso de espacio crece rápidamente. Un +servidor que contenga repositorios de Git que no sean reempacados +rigurosa y frecuentemente requerirá trabajo intenso de disco durante +las copias de seguridad, y ha habido situaciones en copias de +seguridad diaria que toman más de~24 horas como resultado. Un +repositorio recién reempacado de Git es un poco más pequeño que un +repositorio de Mercurial, pero un repositorio sin reempacar es varios +órdenes de magnitud más grande. + +El corazón de Git está escrito en C. Muchas órdenes de Git están +implementadas como guiones de línea de comandos o de Perl y la calidad de esos +guiones varía ampliamente. He encontrado muchas situaciones en las +cuales los guiones no tuvieron en cuenta la presencia de errores que +podrían haber sido fatales. + +Mercurial puede importar el historial de revisiones de un repositorio +de Git. + +\subsection{CVS} + +CVS es probablemente la herramienta de control de revisiones más +ampliamente usada en el planeta. Debido a su edad y su poca pulcritud +interna, ha sido ligeramente mantenida en muchos años. + +Tiene una arquitectura centralizada cliente/servidor. No agrupa +cambios relacionados en consignaciones atómicas, pemitiendo que con +facilidad la gente ``rompa la construcción'': una persona puede +consignar exitósamente parte del cambio y estar bloqueada por la +necesidad de una mezcla, forzando a otras personas a ver solamente una +porción del trabajo que estaban buscando hacer. Esto afecta también +la forma como usted trabaja con el historial del proyecto. Si quiere +ver todas las modificaciones que alguien hizo como parte de una tarea, +necesitará inspeccionar manualmente las descripciones y las marcas de +tiempo de cambio de cada fichero involucrado (esto, si usted saber +cuáles eran tales ficheros). + +CVS tiene una noción confusa de etiquetas y ramas que yo no trataría +incluso de describir. No soporta renombramiento de ficheros o +directorios adecuadamente, facilitando el corromper un +repositorio. Casi no tiene chequeo de consistencia interna, por lo +tanto es casi imposible identificar por que o cómo se corrompió un +repositorio. Yo no recomendaría un repositorio de CVS para proyecto +alguno, ni existente ni nuevo. + +Mercurial puede importar el historial de revisiones de CVS. De todas +maneras hay ciertas precauciones que aplican; las cuales también son +necesarias para cualquier herramienta importadora de historial de +CVS. Debido a la falta de atomicidad de cambios y el no versionamiento +de la jerarquía del sistema de ficheros, es imposible reconstruir +completamente el historial de CVS con precisión; hay cierto trabajo de +conjetura involucrado y los renombramientos tampoco se +mostrarán. Debido a que gran parte de la administración avanzada de +CVS tiene que hacerse manualmente y por lo tanto es proclive al error, +es común que los importadores de CVS encuentren muchos problemas con +repositorios corruptos (marcas de tiempo totalmente desubicadas y +ficheros que han permanecido con candados por más de una década son +dos de los problemas menos interesantes de los que puedo retomar de mi +experiencia personal). + +Mercurial puede importar el historial de revisiones de un repositorio +CVS. + +\subsection{Herramientas comerciales} + +Perforce tiene una arquitectura centralizada cliente/servidor sin +almacenamiento de dato alguno de caché en el lado del cliente. A diferencia de +las herramientas modernas de control de revisiones, Perforce requiere +que un usuario ejecute un comando para informar al servidor acerca de +todo fichero que se vaya a editar. + +El rendimiento de Perforce es muy bueno para equipos pequeños, pero se +degrada rápidamente cuando el número de usuarios va más allá de pocas +docenas. Instalaciones modestamente grandes de Perforce requiere la +organización de proxies para soportar la carga que sus usuarios generan. + +\subsection{Elegir una herramienta de control de revisiones} + +Con la excepción de CVS, toda las herramientas que se han listado +anteriormente tienen fortalezas únicas que las hacen valiosas de acuerdo al +tipo de trabajo. No hay una única herramienta de control de revisiones +que sea la mejor en todas las situaciones. + +Por ejemplo, Subversion es una buena elección para trabajar con +edición frecuente de ficheros binarios, debido a su naturaleza +centralizada y soporte para poner candados a ficheros. + +Personalmente encuentro las propiedades de simplicidad, desempeño, y +buen soporte de fusiones de Mercurial una combinación llamativa que ha +dado buenos frutos por varios años. + + +\section{Migrar de otra herramienta hacia Mercurial} + +Mercurial viene con una extensión llamada \hgext{convert}, que puede +importar historiales de revisiones de forma incremental desde varias +herramientas de control de revisiones. Por ``incremental'', quiero +decir que puede migrar toda el historial de un proyecto en una primera +instancia y después volver a ejecutar la migración posteriormente para +obtener los nuevos cambios que han sucedido después de la migración +inicial. + +A continuación presentamos las herramientas de revisiones que soporta +el comando \hgext{convert}: +\begin{itemize} +\item Subversion +\item CVS +\item Git +\item Darcs +\end{itemize} + +Adicionalmente, \hgext{convert} puede exportar cambios de Mercurial +hacia Subversion. Lo que hace posible probar Subversion y Mercurial +en paralelo antes de lanzarse a un migración total, sin arriesgarse a +perder trabajo alguno. + +El comando \hgxcmd{conver}{convert} es sencillo de usar. Basta con +apuntarlo hacia la ruta o el URL del repositorio fuente, opcionalmente +darle el nombre del nombre del repositorio destino y comenzará a hacer +su trabajo. Después de la conversión inicial, basta con invocar de +nuevo el comando para importar cambios nuevos. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/kdiff3.png Binary file es/kdiff3.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 es/license.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/license.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,142 @@ +\chapter{Licencia de Publicación Abierta} +\label{cha:opl} + +Versión 1.0, 8 Junio de 1999 + +\section{Requerimientos en versiones modificadas y no modificadas} + +Los trabajos bajo Publicación Abierta pueden reproducirse y +distribuirse enteros o en porciones, en cualquier medio físico o +electrónico, siempre y cuando se respeten los términos de esta +licencia, y se incorpore esta licencia o su referencia (con cualquiera +de las opciones elegidas por el autor y el editor) en la reproducción. + +A continuación mostramos la forma correcta de incorporar por referencia: + +\begin{quote} + Copyright (c) \emph{año} por \emph{nombre del autor o designado}. + Este material puede distribuirse solamente bajo los términos y + condiciones especificados por la Licencia de Publicación Abierta, + v\emph{x.y} o posterior (la última versión disponible está en + \url{http://www.opencontent.org/openpub/}). +\end{quote} + +La referencia debe estar seguida inmediatamente por cualquier opción +elegida por el(os) autor(es) y/o editor(es) del documento (consulte la +sección~\ref{sec:opl:options}). + +Se permite la redistribución comercial de los materiales sujetos a la +Publicación Abierta. + +Cualquier publicación en forma estándar de libro (papel) requerirá +citar al editor y autor original. Los nombres del editor y el autor +aparecerán en todas las superficies externas del libro. En todas las +superficies externas el nombre del editor deberá aparecer en tamaño de +la misma medida que el título del trabajo y será citado como poseedor +con respecto al título. + +\section{Derechos de reproducción} + +El derecho de reproducción de cada Publicación Abierta pertenece +al(os) autor(es) o designados. + +\section{Alcance de la licencia} + +Los términos de licencia dsecritos aplican a todos los trabajos bajo +licencia de publicación abierta a menos que se indique de otra forma +en este documento. + +La simple agregación de trabajos de Publicación Abierta o una porción +de trabajos de Publicación Abierta con otros trabajos o programas en +el mismo medio no causarán que esta licencia se aplique a los otros +trabajos. Los agregados deberán contener una nota que especifique la +inclusión de matrial de Publicación Abierta y una nota de derechos de +reproducción acorde. + +\textbf{Separabilidad}. Si cualquier porción de esta licencia no es +aplicable en alguna jurisdicción, las porciones restantes se +mantienen. + +\textbf{Sin garantía}. Los trabajos de Publicación Abierta se +licencian y ofrecen ``como están'' sin garantía de ninguna clase, +expresa o implícita, incluyendo, pero no limitados a las garantías de +mercabilidad y adaptabilidad para un propósito particular o garantía +de no infracción. + +\section{Requerimientos sobre trabajos modificados} + +Todas las versiones modificadas de documentos cubiertos por esta +licencia, incluyendo traducciones, antologías, compilaciones y +documentos parciales, deben seguir estos requerimientos: + +\begin{enumerate} +\item La versión modificada debe estar etiquetada como tal. +\item La persona que hace la modificación debe estar identificada y la + modificación con fecha. +\item El dar crédito al autor original y al editor si se requiere de + acuerdo a las prácticas académicas de citas. +\item Debe identificarse el lugar del documento original sin + modificación. +\item No puede usarse el(os) nombre(s) del autor (de los autores) para + implicar relación alguna con el documento resultante sin el permiso + explícito del autor (o de los autores). +\end{enumerate} + +\section{Recomendaciones de buenas prácticas} + +Adicional a los requerimientos de esta licencia, se solicita a los +redistribuidores y se recomienda en gran medida que: + +\begin{enumerate} +\item Si está distribuyendo trabajaos de Publicación Abierta en copia + dura o CD-ROM, envíe una notificación por correo a los autores + acerca de su intención de redistribuir por lo menos con treinta días + antes de que su manuscrito o el medio se congelen, para permitir a + los autores tiempo para proveer documentos actualizados. Esta + notificación debería describir las modificaciones, en caso de que + haya, al documento. +\item Todas las modificaciones sustanciales (incluyendo eliminaciones) + deben estar marcadas claramente en el documento o si no descritas en + un adjunto del documento. +\item Finalmente, aunque no es obligatorio bajo esta licencia, se + considera de buenos modales enviar una copia gratis de cualquier + expresión en copia dura o CD-ROM de un trabajo licenciado con + Publicación Abierta a el(os) autor(es). +\end{enumerate} + +\section{Opciones de licencia} +\label{sec:opl:options} + +El(os) autor(es) y/o editor de un documento licenciado con Publicación +Abierta pueden elegir ciertas opciones añadiendo información a la +referencia o a la copia de la licencia. Estas opciones se consideran +parte de la instancia de la licencia y deben incluirse con la +licencia (o su incorporación con referencia) en trabajos derivados. + +\begin{enumerate}[A] +\item Prohibir la distribución de versiones substancialmente + modificadas sin el permiso explícito del(os) autor(es). Se definen + ``modificaciones substanciales'' como cambios en el contenido + semántico del documento, y se excluyen simples cambios en el formato + o correcciones tipográficas. + + Para lograr esto, añada la frase ``Se prohibe la distribución de + versiones substancialmente modificadas de este documento sin el + permiso explícito del dueño de los derechos de reproducción.'' a la + referencia de la licencia o a la copia. + +\item Está prohibido prohibir cualquier publicación de este trabajo o + derivados como un todo o una parte en libros estándar (de papel) con + propósitos comerciales a menos que se obtenga un permiso previo del + dueño de los derechos de reproducción. + + Para lograrlo, añada la frase ``La distribución del trabajo o + derivados en cualquier libro estándar (papel) se prohibe a menos que + se obtenga un permiso previo del dueño de los derechos de + reproducción.'' a la referencia de la licencia o la copia. +\end{enumerate} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/metadata.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitácora de cambios + Manifiesto + Bitácora de archivos + + diff -r 5981a0f7540a -r 019040fbf5f5 es/mq-collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-collab.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,428 @@ +\chapter{Usos avanzados de las Colas de Mercurial} +\label{chap:mq-collab} + +Auunque es fácil aprender los usos más directos de las Colas de +Mercurial, tener algo de disciplina junto con algunas de las +capacidadees menos usadas de MQ hace posible trabajar en entornos de +desarrollo complejos. + +En este capítulo, usaré como ejemplo una técnica que he usado para +administrar el desarrollo de un controlador de dispositivo Infiniband +para el kernel de Linux. El controlador en cuestión es grande +(al menos en lo que se refiere a controladores), con 25,000 líneas de +código esparcidas en 35 ficheros fuente. Es mantenido por un equipo +pequeño de desarrolladores. + +Aunque mucho del material en este capítulo es específico de Linux, los +mismos principios aplican a cualquier base de código de la que usted +no sea el propietario principal, y sobre la que usted necesita hacer +un montón de desarrollo. + +\section{El problema de múltiples objetivos} + +El kernel de Linux cambia con rapidez, y nunca ha sido estable +internamente; los desarrolladores hacen cambios drásticos entre +%TODO no encontré una traducción adecuada para "release". Por eso el +%cambio +versiones frecuentemente. Esto significa que una versión del +controlador que funciona bien con una versión particular del kernel ni +siquiera \emph{compilará} correctamente contra, típicamente, cualquier +otra versión. + +Para mantener un controlador, debemos tener en cuenta una buena +cantidad de versiones de Linux en mente. +\begin{itemize} +\item Un objetivo es el árbol de desarrollo principal del kernel de + Linux. En este caso el mantenimiento del código es compartido + parcialmente por otros desarrolladores en la comunidad del kernel, + %TODO drive-by. + quienes hacen modificaciones ``de-afán'' al controlador a medida que + desarrollan y refinan subsistemas en el kernel. + %TODO backport +\item También mantenemos algunos ``backports'' para versiones antiguas + del kernel de Linux, para dar soporte a las necesidades de los + clientes que están corriendo versiones antiguas de Linux que no + incorporan nuestros controladores. (Hacer el \emph{backport} de un + pedazo de código es modificarlo para que trabaje en una versión + de su entorno objetivo anterior a aquella para la cual fue escrito.) +\item Finalmente, nosotros liberamos nuestro software de acuerdo a un + cronograma que no necesariamente está alineado con el que usan los + distribuidores de Linux y los desarrolladores del kernel, así que + podemos entregar nuevas características a los clientes sin forzarlos + a actualizar kernels completos o distribuciones. +\end{itemize} + +\subsection{Aproximaciones tentadoras que no funcionan adecuadamente} + +Hay dos maneras estándar de mantener una porción de software que debe +funcionar en muchos entornos diferentes. + +La primera es mantener varias ramas, cada una pensada para un único +entorno. El problema de esta aproximación es que usted debe tener una +disciplina férrea con el flujo de cambios entre repositorios. Una +nueva característica o un arreglo de fallo deben empezar su vida en un +repositorio ``prístino'', y luego propagarse a cada repositorio de +backport. Los cambios para backports están más limitados respecto a +las ramas a las que deberían propagarse; un cambio para backport que +es aplicado a una rama en la que no corresponde probablemente hará que +el controlador no compile. + +La segunda es mantener un único árbol de código fuente lleno de +declaraciones que activen o desactiven secciones de código dependiendo +del entorno objetivo. Ya que estos ``ifdefs'' no están permitidos en +el árbol del kernel de Linux, debe seguirse algún proceso manual o +automático para eliminarlos y producir un árbol limpio. Una base de +código mantenida de esta manera se convierte rápidamente en un nido de +ratas de bloques condicionales que son difíciles de entender y +mantener. + +%TODO canónica? +Ninguno de estos enfoques es adecuado para situaciones en las que +usted no es ``dueño'' de la copia canónica de un árbol de fuentes. En +el caso de un controlador de Linux que es distribuido con el kernel +estándar, el árbol de Linux contiene la copia del código que será +considerada por el mundo como la canónica. La versión oficial de +``mi'' controlador puede ser modificada por gente que no conozco, sin +que yo siquiera me entere de ello hasta después de que los cambios +aparecen en el árbol de Linus. + +Estos enfoques tienen la debilidad adicional de dificultar la +%TODO upstream. no no es río arriba +generación de parches bien formados para enviarlos a la versión +oficial. + +En principio, las Colas de Mercurial parecen ser un buen candidato +para administrar un escenario de desarrollo como el de arriba. Aunque +este es de hecho el caso, MQ tiene unas cuantas características +adicionales que hacen el trabajo más agradable. + +\section{Aplicar parches condicionalmente mediante guardias} + +Tal vez la mejor manera de conservar la cordura con tantos entornos +objetivo es poder escoger parches específicos para aplicar para cada +situación. MQ provee una característica llamada ``guardias'' +(que se origina del comando \texttt{guards} de Quilt) que hace +precisamente ésto. Para empezar, creemos un repositorio sencillo para +experimentar. +\interaction{mq.guards.init} +Esto nos brinda un pequeño repositorio que contiene dos parches que no +tienen ninguna dependencia respecto al otro, porque tocan ficheros +diferentes. + +La idea detrás de la aplicación condicional es que usted puede +``etiquetar'' un parche con un \emph{guardia}, que simplemente es una +cadena de texto de su elección, y luego decirle a MQ que seleccione +guardias específicos para usar cuando aplique parches. MQ entonces +aplicará, u omitirá, un parche vigilado, dependiendo de los guardias +que usted haya seleccionado. + +Un parche puede tener una cantidad arbitraria de guardias; cada uno es +\emph{positivo} (``aplique el parche si este guardia es +seleccionado'') o \emph{negativo} (``omita este parche si este guardia +es seleccionado''). Un parche sin guardias siempre es aplicado. + +\section{Controlar los guardias de un parche} + +%TODO tal vez no decir determinar, sino definir? +El comando \hgxcmd{mq}{qguard} le permite determinar qué guardias +deben aplicarse a un parche, o mostrar los guardias que están en +efecto. Sin ningún argumento, el comando muestra los guardias del +parche actual de la parte más alta de la pila. +\interaction{mq.guards.qguard} +Para poner un guardia positivo en un parche, prefije el nombre del +guardia con un ``\texttt{+}''. +\interaction{mq.guards.qguard.pos} +Para poner un guardia negativo en un parche, prefije el nombre del +guardia con un ``\texttt{-}''. +\interaction{mq.guards.qguard.neg} + +\begin{note} + El comando \hgxcmd{mq}{qguard} \emph{pone} los guardias en un + parche; no los \emph{modifica}. Esto significa que si usted ejecuta + \hgcmdargs{qguard}{+a +b} sobre un parche, y luego + \hgcmdargs{qguard}{+c} en el mismo parche, el único guardia sobre el + parche después del comando será \texttt{+c}. +\end{note} + +Mercurial almacena los guardias en el fichero \sfilename{series}; la +forma en que son almacenados es fácil tanto de entender como de editar +a mano. (En otras palabras, usted no tiene que usar el comando +\hgxcmd{mq}{qguard} si no lo desea; está bien simplemente editar el +fichero \sfilename{series}) +\interaction{mq.guards.series} + +\section{Selecccionar los guardias a usar} + +%TODO tal vez no decir determinar, sino definir? +El comando \hgxcmd{mq}{qselect} determina qué guardias están activos +en cualquier momento. El efecto de esto es determinar qué parches +aplicará MQ la próxima vez que usted ejecute \hgxcmd{mq}{qpush}. No +tiene ningún otro efecto; en particular, no hace nada a los parches +que ya han sido aplicados. + +Sin argumentos, el comando \hgxcmd{mq}{qselect} lista los guardias en +efecto actualmente, uno por cada línea de salida. Cada argumento es +tratado como el nombre de un guardia a aplicar. +\interaction{mq.guards.qselect.foo} +Si está interesado, los guardias seleccionados actualmente están +almacenados en el fichero \sfilename{guards}. +\interaction{mq.guards.qselect.cat} +Podemos ver el efecto que tienen los guardias seleccionados cuando +ejecutamos \hgxcmd{mq}{qpush}. +\interaction{mq.guards.qselect.qpush} + +Un guardia no puede empezar con un caracter ``\texttt{+}'' o +``\texttt{-}''. El nombre del guardia no debe contener espacios en +blanco, pero muchos otros caracteres son aceptables. Si usted trata de +usar un guardia con un nombre inválido, MQ se quejará: +\interaction{mq.guards.qselect.error} +Cambiar los guardias seleccionados cambia los parches que son +aplicados. +\interaction{mq.guards.qselect.quux} +Usted puede ver en el ejemplo de abajo que los guardias negativos +tienen precedencia sobre los guardias positivos. +\interaction{mq.guards.qselect.foobar} + +\section{Reglas de MQ para aplicar parches} + +Las reglas que MQ usa para decidir si debe aplicar un parche son las +siguientes. +\begin{itemize} +\item Un parche sin guardias es aplicado siempre. +\item Si el parche tiene algún guardia negativo que corresponda con + cualquiera de los guardias seleccionados, se salta el parche. +\item Si el parche tiene algún guardia positivo que corresponda con + cualquiera de los guardias seleccionados, se aplica el parche. +\item Si el parche tiene guardias positivos o negativos, pero ninguno + corresponde con cualquiera de los guardias seleccionados, se salta + el parche. +\end{itemize} + +\section{Podar el entorno de trabajo} + +En el trabajo del controlador de dispositivo que mencioné +anteriormente, yo no aplico los parches a un árbol normal del kernel +de Linux. En cambio, uso un repositorio que sólo contiene una +instantánea de los ficheros fuente y de cabecera que son relevantes +para el desarrollo de Infiniband. Este repositorio tiene un~1\% del +tamaño del repositorio del kernel, por lo que es más fácil trabajar +con él. + +Luego escojo una versión ``base'' sobre la cual son aplicados los +parches. Es una instantánea del árbol del kernel de Linux en una +revisión de mi elección. Cuando tomo la instantánea, almaceno el ID de +conjunto de cambios en el mensaje de consignación. Ya que la +instantánea preserva la ``forma'' y el contenido de las partes +relevantes del árbol del kernel, puedo aplicar mis parches sobre mi +pequeño repositorio o sobre un árbol normal del kernel. + +Normalmente, el árbol base sobre el que se aplican los parches debería +ser una instantánea de un árbol de desarrollo muy reciente. Esto +facilita mucho el desarrollo de parches que puedan ser enviados al +árbol oficial con pocas o ninguna modificación. + +\section{Dividir el fichero \sfilename{series}} + +Yo categorizo los parches en el fichero \sfilename{series} en una +serie de grupos lógicos. Cada sección de parches similares empieza con +un bloque de comentarios que describen el propósito de los parches que +le siguen. + +La secuencia de grupos de parches que mantengo se muestra a +continuación. El orden de los grupos es importante; explicaré porqué +luego de que presente los grupos. +\begin{itemize} +\item El grupo ``aceptado''. Son parches que el equipo de desarrollo + ha enviado al mantenedor del subsistema Infiniband, y que él ha + aceptado, pero que no están presentes en la instantánea en la cual + está basada el repositorio pequeño. Estos son parches de + ``sólo lectura'', presentes únicamente para transformar el árbol en + un estado similar al del repositorio del mantenedor oficial. +\item El grupo ``revisar''. Parches que yo he enviado, pero sobre los + que que el mantenedor oficial ha solicitado modificaciones antes de + aceptarlos. +\item El grupo ``pendiente''. Parches que no he enviado al mantenedor + oficial, pero que ya están terminados. Estos parches serán de + ``sólo lectura'' por un buen tiempo. Si el mantenedor oficial los + acepta cuando los envíe, los moveré al final del grupo ``aceptado''. + Si él solicita que modificaciones en alguno de ellos, los moveré al + principio del grupo ``revisar''. +\item El grupo ``en proceso''. Parches que están siendo activamente + desarrollados, y no deberían ser enviados a ninguna parte aún. +\item El grupo ``backport''. Parches que adaptan el árbol de fuentes a + versiones antiguas del árbol del kernel. +\item El grupo ``no enviar''. Parches que por alguna razón nunca deben + ser enviados al mantenedor oficial del kernel. Por ejemplo, alguno + de esos parches podría cambiar las cadenas de identificación + embebidas del controlador para hacer más fácil la distinción, en + pruebas de campo, entre una versión del controlador de + salida-del-árbol y una versión entregada por un vendedor de alguna + distribución. +\end{itemize} + +Ahora volvemos a las razones para ordenar los grupos de parches en +esta manera. Quisiéramos que los parches del fondo de la pila sean tan +estables como sea posible, para no tener que revisar parches más +arriba debido a cambios de contexto. Poner los parches que nunca +cambiarán en el primer lugar del fichero \sfilename{series} sirve a +este propósito. + +También desearíamos que los parches que sabemos que debemos modificar +sean aplicados sobre un árbol de fuentes que se parezca al oficial +tanto como sea posible. Es por esto que mantenemos los parches +aceptados disponibles por una buena cantidad de tiempo. + +Los parches ``backport'' y ``no enviar'' flotan al final del fichero +\sfilename{series}. Los parches de backport deben ser aplicados encima +de todos los otros parches, y los parches ``no enviar'' pueden +perfectamente quedarse fuera del camino. + +\section{Mantener la serie de parches} + +En mi trabajo, uso varios guardias para controlar qué parches deben +ser aplicados. + +\begin{itemize} +\item Los parches ``aceptados'' son vigilados con + \texttt{accepted}. Yo habilito este guardia la mayoría de las veces. + Cuando aplico los parches sobre un árbol donde los parches ya están + %TODO no será ``desactivar este guardia''? si sí, corregir versión + %en inglés también + presentes, puedo desactivar este parche, y los parches que lo siguen + se aplicarán sin problemas. +\item Los parches que están ``terminados'', pero no han sido enviados, + no tienen guardias. Si estoy aplicando la pila de parches a una + copia del árbol oficial, no necesito habilitar ningún guardia para + obtener un árbol de fuentes razonablemente seguro. +\item Los parches que necesitan revisión antes de ser reenviados + tienen el guardia \texttt{rework}. +\item Para aquellos parches que aún están bajo desarrollo, uso + \texttt{devel}. +\item Un parche de backport puede tener varios guardias, uno para cada + versión del kernel a la que aplica. Por ejemplo, un parche que hace + backport de un segmento de código a~2.6.9 tendrá un guardia~\texttt{2.6.9}. +\end{itemize} +La variedad de guardias me brinda una flexibilidad considerable para +determinar qué tipo de árbol de fuentes acabaré por obtener. En la +mayoría de las situaciones, la selección de guardias apropiados es +automatizada durante el proceso de compilación, pero puedo ajustar +manualmente los guardias a usar para circunstancias poco comunes. + +\subsection{El arte de escribir parches de backport} + +Al usar MQ, escribir un parche de backport es un proceso simple. Todo +lo que dicho parche debe hacer es modificar una sección de código que +usa una característica del kernel que no está presente en la versión +anterior del kernel, para que el controlador siga funcionando +correctamente en esa versión anterior. + +Una meta útil al escribir un buen parche de backport es hacer parecer +que el código hubiera sido escrito para la versión vieja del kernel +que usted tiene como objetivo. Entre menos intrusivo el parche, más +fácil será entenderlo y mantenerlo. Si usted está escribiendo una +colección de parches de backport para evitar el efecto de ``nido de +ratas'' de tener muchos \texttt{\#ifdef}s (secciones de código fuente +que sólo son usados condicionalmente) en su código, no introduzca +\texttt{\#ifdef}s dependientes de versiones específicas en los +parches. En vez de eso, escriba varios parches, cada uno de ellos +haciendo cambios incondicionales, y controle su aplicación usando +guardias. + +Hay dos razones para ubicar los parches de backport en un grupo +diferente, aparte de los parches ``regulares'' cuyos efectos son +modificados por ellos. La primera es que mezclar los dos hace más +difícil usar herramientas como la extensión \hgext{patchbomb} para +automatizar el proceso de enviar los parches a un mantenedor oficial. +La segunda es que un parche de backport puede perturbar el contexto en +el que se aplica un parche regular subsecuente, haciendo imposible +aplicar el parche normal limpiamente \emph{sin} que el parche de +backport sea aplicado antes. + +\section{Consejos útiles para hacer desarrollo con MQ} + +\subsection{Organizar parches en directorios} + +Si está trabajando en un proyecto grande con MQ, no es difícil +acumular un gran número de parches. Por ejemplo, tengo un repositorio +de parches que contiene más de 250 parches. + +Si usted puede agrupar estos parches en categorías lógicas separadas, +usted puede almacenarlos en diferentes directorios si lo desea; MQ no +tiene problemas manejando nombres de parches que contienen separadores +de ruta. + +\subsection{Ver el historial de un parche} +\label{mq-collab:tips:interdiff} + +Si usted está desarrollando un conjunto de parches en un período de +tiempo grande, es una buena idea mantenerlos en un repositorio, como +se discutió en la sección~\ref{sec:mq:repo}. Si lo hace, notará +rápidamente que usar el comando \hgcmd{diff} para mirar el historial +del repositorio no es viable. Esto es debido en parte a que usted está +mirando la segunda derivada del código real (el diff de un diff), pero +también porque MQ añade ruido al proceso al modificar las marcas de +tiempo y los nombres de directorio cuando actualiza un parche. + +Sin embargo, usted puede usar la extensión \hgext{extdiff}, que es +provisto junto con Mercurial, para convertir un diff de dos versiones +de un parche en algo legible. Para hacer esto, usted necesitará un +paquete de un tercero llamado +\package{patchutils}~\cite{web:patchutils}. Éste paquete provee un +comando llamado \command{interdiff}, que muestra las diferencias entre +dos diffs como un diff. Al usarlo en dos versiones del mismo diff, +genera un diff que representa el diff de la primera a la segunda +versión. + +Usted puede habilitar la extensión \hgext{extdiff} de la manera usual, +añadiendo una línea a la sección \rcsection{extensions} de su \hgrc. +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} +El comando \command{interdiff} espera recibir los nombres de dos +ficheros, pero la extensión \hgext{extdiff} le pasa un par de +directorios al programa que ejecuta, cada uno de los cuales puede +contener una cantidad arbitraria de ficheros. Por esto necesitamos un +programa pequeño que ejecute \command{interdiff} en cada par de +ficheros de estos dos directorios. Este programa está disponible como +\sfilename{hg-interdiff} en el directorio \dirname{examples} del +repositorio de código fuente que acompaña a este libro. +\excode{hg-interdiff} + +Con el programa \sfilename{hg-interdiff} en la ruta de búsqueda de su +intérprete de comandos, puede ejecutarlo como sigue, desde dentro de +un directorio de parches MQ: +\begin{codesample2} + hg extdiff -p hg-interdiff -r A:B my-change.patch +\end{codesample2} +Ya que usted seguramente querrá usar este comando tan largo a menudo, +puede hacer que \hgext{hgext} lo haga disponible como un comando +normal de Mercurial, editando de nuevo su \hgrc. +\begin{codesample2} + [extdiff] + cmd.interdiff = hg-interdiff +\end{codesample2} +Esto le indica a \hgext{hgext} que ponga a disposición un comando +\texttt{interdiff}, con lo que usted puede abreviar la invocación +anterior de \hgxcmd{extdiff}{extdiff} a algo un poco más manejable. +\begin{codesample2} + hg interdiff -r A:B my-change.patch +\end{codesample2} + +\begin{note} + %TODO revisar redacción + El comando \command{interdiff} trabaja bien sólo si los ficheros + contra los cuales son generadas las versiones de un parche se + mantienen iguales. Si usted crea un parche, modifica los ficheros + subyacentes, y luego regenera el parche, \command{interdiff} podría + no producir ningún resultado útil. +\end{note} + +La extensión \hgext{extdiff} es útil para más que solamente mejorar la +presentación de los parches~MQ. Para leer más acerca de esto, vaya a +la sección~\ref{sec:hgext:extdiff}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/mq-ref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-ref.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,378 @@ +\chapter{Referencia de las Colas de Mercurial} +\label{chap:mqref} + +\section{Referencia de órdenes MQ} +\label{sec:mqref:cmdref} + +Si desea dar un vistazo a las órdenes que ofrece MQ, use la orden +\hgcmdargs{help}{mq}. + +\subsection{\hgxcmd{mq}{qapplied}---imprimir los parches aplicados} + +La orden \hgxcmd{mq}{qapplied} imprime la pila actual de parches +aplicados. Los parches se imprimen en orden de antigüedad, primero +los más antiguos y después los más recientes, por lo tanto el último +parche de la lista es el que está en el ``tope''. + +\subsection{\hgxcmd{mq}{qcommit}---consignar cambios en la cola del repositorio} + +La orden \hgxcmd{mq}{qcommit} consigna cualquier cambio sobresaliente +en el repositorio \sdirname{.hg/patches}. Esta orden solamente +funciona si el directorio \sdirname{.hg/patches} es un repositorio, +p.e.~usted creó el directorio con +\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} o ejecutó +\hgcmd{init} en el directorio después de correr \hgxcmd{mq}{qinit}. + +Esta orden es un atajo para \hgcmdargs{commit}{--cwd .hg/patches}. + +\subsection{\hgxcmd{mq}{qdelete}---eliminar un parche del fichero + \sfilename{series}} + +La orden \hgxcmd{mq}{qdelete} elimina la entrada del fichero +\sfilename{series} para el parche en el directorio +\sdirname{.hg/patches}. No sca el parche si ha sido aplicado. De +forma predeterminada no borra el fichero del parche; use la opción +\hgxopt{mq}{qdel}{-f} para hacerlo. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qdel}{-f}] Elimina el fichero del parche. +\end{itemize} + +\subsection{\hgxcmd{mq}{qdiff}---imprimir la diferencia del último + parche aplicado} + +La orden \hgxcmd{mq}{qdiff} imprime un diff del parche más +recientemente aplicado. Es equivalente a \hgcmdargs{diff}{-r-2:-1}. + +\subsection{\hgxcmd{mq}{qfold}---fusionar (``integrar'') varios parches en + uno solo} + +La orden \hgxcmd{mq}{qfold} fusiona muchos parches en el último parche +aplicado, de tal forma que el último parche aplicado es la unión de +todos los cambios de los parches en cuestión. + +Los parches a fusionar no deben haber sido aplicados; +\hgxcmd{mq}{qfold} saldrá indicando un error si alguno ha sido +aplicado. El orden en el cual los parches se pliegan es +significativo; \hgcmdargs{qfold}{a b} significa ``aplique el parche +más reciente, seguido de \texttt{a}, y seguido de \texttt{b}''. + +Los comentarios de los parches integrados se colocan al final de los +comentarios del parche destino, con cada bloque de comentarios +separado con tres asteriscos (``\texttt{*}''). Se usa la opción +\hgxopt{mq}{qfold}{-e} para editar el mensaje de consignación para el +conjunto de cambios/parches después de completarse el pliegue. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qfold}{-e}] Edita el mensaje de consignación y la + descripción del parche del parche que se ha integrado. +\item[\hgxopt{mq}{qfold}{-l}] Usa los contenidos del fichero dado como + el nuevo mensaje de consignación y descripción del parche para el + parche a integrar. +\item[\hgxopt{mq}{qfold}{-m}] Usa el texto dado como el mensaje de + consignación y descripción del parche para el parche integrado. +\end{itemize} + +\subsection{\hgxcmd{mq}{qheader}---desplegar el encabezado/descripción + de un parche} + +La orden \hgxcmd{mq}{qheader} imprime el encabezado o descripción de +un parche. De forma predeterminada, imprime el encabezado del último +parche aplicado. Si se da un argumento, imprime el encabezado del +parche referenciado. + +\subsection{\hgxcmd{mq}{qimport}---importar el parche de un tercero en + la cola} + +La orden \hgxcmd{mq}{qimport} añade una entrada de un parche externo +al fichero \sfilename{series} y copia el parche en el directorio +\sdirname{.hg/patches}. Añade la entrada inmediatamente después del +último parche aplicado, pero no introduce el parche. + +Si el directorio \sdirname{.hg/patches} es un repositorio, +\hgxcmd{mq}{qimport} automáticamente hace un \hgcmd{add} del parche +importado. + +\subsection{\hgxcmd{mq}{qinit}---preparar un repositorio para trabajar + con MQ} + +La orden \hgxcmd{mq}{qinit} prepara un repositorio para trabajar con +MQ. Crea un directorio llamado \sdirname{.hg/patches}. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qinit}{-c}] Crea \sdirname{.hg/patches} como un + repositorio por sí mismo. También crea un fichero + \sfilename{.hgignore} que ignorará el fichero \sfilename{status}. +\end{itemize} + +Cuando el directorio \sdirname{.hg/patches} es un repositorio, las órdenes +\hgxcmd{mq}{qimport} y \hgxcmd{mq}{qnew} hacen \hgcmd{add} +automáticamente a los parches nuevos. + +\subsection{\hgxcmd{mq}{qnew}---crear un parche nuevo} + +La orden \hgxcmd{mq}{qnew} crea un parche nuevo. Exige un argumento, +el nombre que se usará para tal parche. El parche recién creado está +vacío inicialmente. Se añade al fichero \sfilename{series} después +del último parche aplicado, y se introduce en el tope de ese parche. + +Si \hgxcmd{mq}{qnew} encuentra ficheros modificados en el directorio +de trabajo, rehusará crear un parche nuevo a meos que se emplee +\hgxopt{mq}{qnew}{-f} la opción (ver más adelante). Este +comportamiento le permite hacer \hgxcmd{mq}{qrefresh} al último parche +aplicado antes de aplicar un parche nuevo encima de este. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qnew}{-f}] Crea un parche nuevo si los contenidos + del directorio actual han sido modificados. Cualquier modificación + significativa se añade al parche recientemente creado, de tal forma + que al finalizar la orden, el directorio de trabajo no lucirá + modificado. +\item[\hgxopt{mq}{qnew}{-m}] Usa el texto dado como el mensaje de + consignación. Este texto se almacenará al principio del fichero del + parche, antes de los datos del parche. +\end{itemize} + +\subsection{\hgxcmd{mq}{qnext}---imprimir el nombre del próximo parche} + +La orden \hgxcmd{mq}{qnext} imprime el nombre del siguiente parche en +el fichero \sfilename{series} a continuación del último parche +aplicado. Este parche sería el próximo parche a aplicar si se +ejecutara la orden \hgxcmd{mq}{qpush}. + +\subsection{\hgxcmd{mq}{qpop}---sustraer parches de la pila} + +La orden \hgxcmd{mq}{qpop} elimina los parches aplicados del tope de +la pila de parches aplicados. De forma predeterminada solamente +remueve un parche. + +Esta orden elimina los conjuntos de cambios que representan los +parches sustraídos del repositorio, y actualiza el directorio de +trabajo para deshacer los efectos de los parches. + +Esta orden toma un argumento opcional, que usa como el nombre o el +índice del parche que desea sustraer. Si se da el nombre, sustraerá +los parches hasta que el parche nombrado sea el último parche +aplicado. Si se da un número, \hgxcmd{mq}{qpop} lo trata como un +índice dentro del fichero \sfilename{series}, contando desde +cero (no cuenta las líneas vacías o aquellas que sean únicamente +comentarios). Sustrae los parches hasta que el parche identificado +por el índice sea el último parche aplicado. + +La orden \hgxcmd{mq}{qpop} no lee o escribe parches en el fichero +\sfilename{series}. \hgxcmd{mq}{qpop} se constituye por tanto en una +forma segura de sustraer un parche del fichero \sfilename{series} o un +parche que ha eliminado o renombrado completamente. En los dos +últimos casos, use el nombre del parche tal como lo hizo cuando lo +aplicó. + +De forma predeterminada, la orden \hgxcmd{mq}{qpop} no sustraerá +parche alguno si el directorio de trabajo ha sido modificado. Puede +modificar este comportamiento con la opción \hgxopt{mq}{qpop}{-f}, que +revierte todas las modificaciones del directorio de trabajo. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qpop}{-a}] Sustrae todos los parches aplicados. + Restaura el repositorio al estado antes de haber aplicado parche alguno. +\item[\hgxopt{mq}{qpop}{-f}] Revertir forzadamente cualquier + modificación del directorio de trabajo cuando se hace sustracciones. +\item[\hgxopt{mq}{qpop}{-n}] Sustraer un parche de la cola dado un nombre. +\end{itemize} + +La orden \hgxcmd{mq}{qpop} elimina una línea del final del fichero +\sfilename{status} por cada parche que se sustrae. + +\subsection{\hgxcmd{mq}{qprev}---imprimir el nombre del parche anterior} + +La orden \hgxcmd{mq}{qprev} imprime el nombre del parche en el fichero +\sfilename{series} que está antes del último parche aplicado. Este +se volverá el último parche aplicado si ejecuta \hgxcmd{mq}{qpop}. + +\subsection{\hgxcmd{mq}{qpush}---introducir parches a la pila} +\label{sec:mqref:cmd:qpush} + +La orden \hgxcmd{mq}{qpush} añade parches a la pila. De forma +predeterminada añade solamente un parche. + +Esta orden crea un conjunto de cambios que representa cada parche +aplicado y actualiza el directorio de trabajo aplicando los efectos de +los parches. + +Los datos predeterminados cuando se crea un conjunto de cambios +corresponde a: +\begin{itemize} +\item La fecha de consignación y zona horaria corresponden a la hora + actual de la zona. Dado que tales datos se usan para computar la + identidad de un conjunto de cambios, significa que si hace + \hgxcmd{mq}{qpop} a un parche y \hgxcmd{mq}{qpush} de nuevo, el + conjunto de cambios que introduzca tendrá una identidad distinta a + la del conjunto de cambios que sustrajo. +\item El autor es el mismo que el predeterminado usado por la orden + \hgcmd{commit}. +\item El mensaje de consignación es cualquier texto del fichero del + parche que viene antes del primer encabezado del diff. Si no hay + tal texto, un mensaje predeterminado se sua para identificar el + nombre del parche. +\end{itemize} +Su un parche contiene un encabezado de parche de Mercurial (XXX add +link), la información en el encabezado del parche tiene precedencia +sobre el predeterminado. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qpush}{-a}] Introduce todos los parches que no han + sido aplicados del fichero \sfilename{series} hasta que no haya nada + más para introducir. +\item[\hgxopt{mq}{qpush}{-l}] Añade el nombre del parche al final del + mensaje de consignación +\item[\hgxopt{mq}{qpush}{-m}] Si un parche no se aplica limpiamente, + usa la entrada para un parche en otra cola almacenada para computar + los parámetros en una fusión de tres, y aplica una fusión de tres + fuentes usando la maquinaria usual de Mercurial. Usa la resolución + de la fusión como el contenido del parche nuevo. +\item[\hgxopt{mq}{qpush}{-n}] Usa la cola mencionada si se está + fusionando en la introducción. +\end{itemize} + +La orden \hgxcmd{mq}{qpush} lee, pero no modifica el fichero +\sfilename{series}. Añade al final del fichero \hgcmd{status} una +línea por cada parche que se introduce. + +\subsection{\hgxcmd{mq}{qrefresh}---actualiza el último parche aplicado} + +La orden \hgxcmd{mq}{qrefresh} actualiza el último parche aplicado. +Modifica el parche, elimina el último conjunto de cambios que +representó el parche, y crea un nuevo conjunto de cambios para +representar el parche modificado. + +La orden \hgxcmd{mq}{qrefresh} busca las siguientes modificaciones: +\begin{itemize} +\item Los cambios al mensaje de consignación, p.e.~el texto antes del + primer encabezado de diff en el fichero del parche, se replejan en + el nuevo conjunto de cambios que representa el parche. +\item Las modificaciones a los ficheros a los que se les da + seguimiento en el directorio de trabajo se añade al parche. +\item Los cambios a los ficheros a los que se les da seguimiento con + \hgcmd{add}, \hgcmd{copy}, \hgcmd{remove}, o \hgcmd{rename}. Se + añaden al parche los ficheros añadidos, copiados y renombrados, + mientras que los ficheros eliminados y las fuentes renombradas se + eliminan. +\end{itemize} + +Incluso si \hgxcmd{mq}{qrefresh} no detecta cambios, de todas maneras +recrea el conjunto de cambios que representa el cambio. Esto causa +que la identidad del conjunto de cambios difiera del conjunto de +cambios previo que identificó al parche. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qrefresh}{-e}] Modificar la descripción de la + consignación y el parche con el editor de texto preferido. +\item[\hgxopt{mq}{qrefresh}{-m}] Modificar el mensaje de consignación + y la descripción del parche con el texto dado. +\item[\hgxopt{mq}{qrefresh}{-l}] Modificar el mensaje de consignación + y la descripción del parche con el texto del fichero dado. +\end{itemize} + +\subsection{\hgxcmd{mq}{qrename}---renombrar un parche} + +La orden \hgxcmd{mq}{qrename} renombra un parche y cambia la entrada +del parche en el fichero \sfilename{series}. + +Con un argumento sencillo, \hgxcmd{mq}{qrename} renombra el último +parche aplicado. Con dos argumentos, renombra el primer argumento con +el segundo. + +\subsection{\hgxcmd{mq}{qrestore}---restaurar el estado almacenado de + la cola} + +XXX No idea what this does. + +\subsection{\hgxcmd{mq}{qsave}---almacena el estado actual de la cola} + +XXX Likewise. + +\subsection{\hgxcmd{mq}{qseries}---imprime la serie completa de parches} + +La orden \hgxcmd{mq}{qseries} imprime la serie completa de parches del +fichero \sfilename{series}. Imprime solamente los nombres de los +parches sin las líneas en blanco o comentarios. Imprime primero el +primero y de último, el último aplicado. + +\subsection{\hgxcmd{mq}{qtop}---imprime el nombre del parche actual} + +\hgxcmd{mq}{qtop} imprime el nombre del último parche aplicado. + +\subsection{\hgxcmd{mq}{qunapplied}---imprimir los parches que aún no + se han aplicado} + +La orden \hgxcmd{mq}{qunapplied} imprime los nombres de los parches +del fichero \sfilename{series} que todavía no han sido aplicados. Los +imprime de acuerdo al orden en el cual serían introducidos. + +\subsection{\hgcmd{strip}---remover una revisión y sus descendientes} + +La orden \hgcmd{strip} remueve una revisión, y todos sus descendientes +del repositorio. Deshace los efectos de las revisiones removidas del +repositorio, y actualiza el directorio de trabajo hasta el primer +padre de la revisión removida. + +La orden \hgcmd{strip} almacena una copia de segurida de los conjuntos +de cambios en un agrupamiento, de forma tal que puedan ser reaplicados +en caso de que se hayan removido por equivocación. + +Opciones: +\begin{itemize} +\item[\hgopt{strip}{-b}] Almacenar conjuntos de cambios no + relacionados que se han mezclado con los conjuntos de cambios que + están en franjas con el agrupamiento de copia de seguridad. +\item[\hgopt{strip}{-f}] Si una rama tiene varias ramas principales + remueve todos los frentes. XXX This should be renamed, y usa + \texttt{-f} para desagrupar revisiones cuando hay cambios pendientes. +\item[\hgopt{strip}{-n}] No almacene la copia de seguridad agrupada. +\end{itemize} + +\section{Referencia de ficheros de MQ} + +\subsection{El fichero \sfilename{series}} + +El fichero \sfilename{series} contiene una lista de los nombres de +todos los parches que MQ puede aplicar. Se representa como una lista +de nombres, uno por línea. Se ignora el espacio en blanco al +principio y al final. + +Las líneas pueden contener comentario. Un comentario comienza con el +caracter ``\texttt{\#}'', y va hasta el final de la línea. Se ignoran +las líneas vacías y las que solamente contengan comentarios. + +En algún momento podría editar el fichero \sfilename{series} a mano, +por tal motivo se admiten comentarios y líneas en blanco como se +menciono anteriormente. Por ejemplo, puede poner en comentario un +parche temporalmente y \hgxcmd{mq}{qpush} omitirá tal parche cuando +los aplique. También puede cambiar el orden en el cual se aplican los +parches, reordenando las entradas en el fichero \sfilename{series}. + +También es posible colocar el fichero \sfilename{series} bajo control +de revisiones; también es favorable colocar todos los parches que refiera +bajo control de revisiones. Si crea un directorio de parches con la +opción \hgxopt{mq}{qinit}{-c} de \hgxcmd{mq}{qinit}, esto se hará +automáticamente. + +\subsection{El fichero \sfilename{status}} + +El fichero \sfilename{status} contiene los nombres y los hashes de los +conjuntos de cambios de todos los parches que MQ ha aplicado. A +diferencia del fichero \sfilename{series}, este NO ha sido diseñado +para ser editado. No debería colocar este fichero bajo el control de +revisiones o modificarlo de forma alguna. MQ lo usa estrictamente +para administración interna. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-stack.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,280 @@ + + + + + + + + + + + image/svg+xml + + + + + + + prevent-compiler-reorder.patch + + namespace-cleanup.patch + + powerpc-port-fixes.patch + + report-devinfo-correctly.patch + { + { + presente en la serie,pero no aplicado + parches aplicados,Conjuntos de cambios presentes + parche aplicadomás recientemente + 201ad3209902 + 126b84e593ae + a655daf15409 + e50d59aaea3a + + forbid-illegal-params.patch + + fix-memory-leak.patch + + diff -r 5981a0f7540a -r 019040fbf5f5 es/mq.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1103 @@ +\chapter{Administración de cambios con Colas de Mercurial} +\label{chap:mq} + +\section{El problema de la administración de parches} +\label{sec:mq:patch-mgmt} + +Un escenario frecuente: usted necesita instalar un paquete de software +desde las fuentes, pero encuentra un fallo que debe arreglar antes de +poder comenzar a usarlo. Hace sus cambios, y se olvida del paquete +por un tiempo, unos meses después necesita actualizar a una nueva +versión del paquete. Si la nueva versión del paquete todavía tiene el +fallo, debe extraer su arreglo del árbol de fuentes anteriores y +aplicarlo a la nueva versión. Una tarea tediosa en la cual es fácil +equivocarse. + +Este es un caso simple del problema del ``manejo de parches''. Usted +tiene un árbol de fuentes del ``mantenedor principal'' que no puede +cambiar: necesita hacer algunos cambios locales sobre el árbol +principal; y desearía poder mantener tales cambios separados, de forma +tal que pueda aplicarlos a versiones más nuevas del árbol principal. + +El problema de administración de parches surge en muchas situaciones. +Probablemente la más visible es cuando un usuario de un proyecto de +software de fuentes abiertas contribuye con un arreglo de un fallo o +una nueva característica a los mantenedores del proyecto en la forma +de un parche. + +Aquellos que distribuyen sistemas operativos que incluyen programas +abiertos usualmente requieren hacer cambios en los paquetes que +distribuyen de tal forma que se armen apropiadamente en sus ambientes. + +Cuando hay pocos cambios por mantener, es muy sencillo administrar un +solo parche con los programas estándar \command{diff} y +\command{patch} (ver la sección~\ref{sec:mq:patch} para ver cómo +emplear tales herramientas). Cuando la cantidad de cambios comienza a +crecer, tiene sentido mantener parches como ``porciones de trabajo'' +individual, de forma que cada cambio contiene solamente un arreglo de +un fallo (el parche puede modificar varios ficheros, pero está +``haciendo una sola cosa''), y puede tener cierta cantidad de tales +parches para diferentes fallos y cambios locales. En esta situación, +si envía un parche que arregla un fallo a los mantenedores principales +de un paquete y ellos incluyen su arreglo en una publicación +posterior, puede deshacerse de tal parche cuando se actualice a la +nueva versión. + +Mantener un solo parche frente a un árbol principal es algo tedioso y +es fácil equivocarse, pero no es difícil. Aunque, la complejidad del +problema crece rápidamente a medida que la cantidad de parches que +tiene que mantener crece. Con más que una pequeña cantidad de +cambios, entender cuáles ha aplicado se convierte de algo desordenado +a algo avasallante. + +Afortunadamente Mercurial provee una extensión poderos: Colas de +Mercurial (o simplemente ``MQ''), que simplifica en gran medida el +problema de administración de parches. + +\section{La prehistoria de las Colas de Mercurial} +\label{sec:mq:history} + +A finales de los 90s, muchos desarrolladores del núcleo de Linux +comenzaron a mantener ``series de parches'' que modificaron el +comportamiento del núcleo de Linux. Algunos se enfocaban en +estabilidad, otros en aumentar las características, y otros un poco +más especulativos. + +Los tamaños de las series de parches crecieron rápidamente. En el +2002, Andrew Morton publicó algunos guiones de línea de órdenes que +estuvo usando para automatizar la tarea de administrar su cola de +parches. Andrew usó exitósamente tales guiones para administrar +centenas (a veces millares) de parches en el núcleo de Linux. + +\subsection{Trabajar parches con quilt} +\label{sec:mq:quilt} + +A comienzos del 2003, Andreas Gruenbacher y Martin Quinson tomaron la +aproximación de los guiones de Andrew y publicaron una herramienta +llamada +``patchwork quilt''~\cite{web:quilt}, o simplemente ``quilt'' +(ver~\cite{gruenbacher:2005} el paper que lo describe). Dado que +quilt automatizaba sustancialmente la administración de parches, fue +adoptado en gran medida por desarrolladores de programas abiertos. + +Quilt maneja una \emph{pila de parches} sobre un árbol de directorios. +Para comenzar, usted le indica a quilt que administre un árbol de +directorios, le indica qué ficheros manejar; Este almacena los nombres +y los contenidos de estos ficheros. Para arreglar un fallo, usted +crea un nuevo parche (con una sola orden), edita los ficheros que está +arreglando y ``refresca'' el parche. + +El paso de refresco hace que quilt revise el árbol de directorios; +actualiza el parche con todos los cambios que usted haya hecho. Puede +crear otro parche sobre el primero, que hará seguimiento de los +cambios requeridos para modificar el árbol desde ``el árbol con un +parch aplicado'' a un ``árbol con dos parches aplicados''. + +Usted puede \emph{elegir} qué cambios desea aplicar al árbol. Si +``pop''\ndt{saca} un parche, los cambios hechos por tal parchve +desapareceŕan del árbol de directorios. Quilt recuerda qué parches ha +sacado, para que pueda ``introducirlos''\ndt{push} posteriormente, así el +árbol de directorios se restaurará con las modificaciones que vienen +del parche. Lo más importante es que puede ejecutar la orden +``refresh'' en cualquier momento, y el último parche será +actualizado. Esto significa que puede, en cualquier momento, cambiar +qué parches serán aplicados y qué modificaciones hacen ellos. + +Quilt no tiene nada que ver con herramientas de control de versiones, +y puede trabajar bien sobre un conjunto de fuentes que viene de un +fichero comprimido y empaquetado o una copia de trabajo de Subversion. + +\subsection{Pasar de trabajo con parches con Quilt hacia Colas de Mercurial} +\label{sec:mq:quilt-mq} + +A mediados de 2005, Chris Mason tomó las características de quilt y +escribió una extensión que llamó Colas de Mercurial\ndt{Mercurial +Queues}, que proporcionó un comportamiento a Mercurial al estilo +quilt. + +La diferencia clave entre quilt y MQ es que quilt no sabe nada acerca +del sistema de control de revisiones, mientras que MQ está +\emph{integrado} con Mercurial. Cada parche que usted introduce se +representa como un conjunto de cambios en Mercurial. Si sustrae un +parche, el conjunto de cambios desaparece.\ndt{introduce originalmente es +push y pop es sustraer en este contexto, usaremos el original en inglés +cuando encontremos que facilita la comprensión} + +Dado que quilt no se preocupa por las herramientas de control de +revisiones, continúa siendo una porción de software tremendamente útil +para aquellas situaciones en las cuales no puede usar Mercurial y MQ. + +\section{La gran ventaja de MQ} + +No puedo sobreestimar el valor que MQ ofrece en la unificación de +parches y el control de revisiones. + +La principal razón por la cual los parches han persistido en el mundo +del software libre y de fuentes abiertas--a pesar de la creciente +disponibilidad de herramientas poderosas de control de revisiones-- es +la \emph{agilidad} que ofrecen. + +Las herramientas tradicionales de control de revisiones llevan un +registro permanente e irreversible de todo lo que usted hace. A pesar +de que esto tiene gran valor, también es bastante sutil. Si requiere +realizar un experimento ((((wild-eyed)))), debe ser cuidadoso en cómo +lo hace, o puede dejar trazas innecesarias--o peor aún, +desconcertantes o desestabilizantes--- de los pasos y errores en el +registro de revisiones de forma permanente. + +En contraste, con la cohesión de MQ con el control de revisiones +distribuidos y los parches, resulta más sencillo aislar su trabajo. +Sus parches viven encima del historial de revisiones normales, y +puede hacer que ellos desaparezcan o reaparezcan cuando lo desee. Si +no le gusta un parche, puede desecharlo. Si un parche no satisface +todo lo que usted desea, puede arreglarlo---tantas veces como lo +requiera, hasta que lo haya refinado lo suficiente hacia sus +expectativas. + +Por ejemplo, la integración de parches con el control de revisiones +hace que el entender los parches y revisar sus efectos---y sus +interacciones con el código en el cuál están enlazados--- sea +\emph{mucho} más sencillo. Dado que todo parche que se aplique tiene +un conjunto de cambios asociado, puede usar +\hgcmdargs{log}{\emph{filename}} para ver qué conjuntos de cambios y +parches afectaron un fichero. Puede usar la orden \hgext{bisect} para +hacer una búsqueda binaria sobre todos los conjuntos de cambios y +parches aplicados para ver dónde se introdujo un fallo o dónde fue +arreglado. Puede usar la orden \hgcmd{annotate} para ver qué +conjuntos de cambios o parches modificaron una línea particular de un +fichero fuente. Y mucho más. + +\section{Entender los parches} +\label{sec:mq:patch} + +Dado que MQ no esconde su naturaleza parche-céntrica, es muy útil para +entender de qué se tratan los parches, y un poco acerca de las +herramientas que trabajan con ellos. + +La orden de Unix tradicional \command{diff} compara dos ficheros, e +imprime una lista de diferencias de sus líneas. La orden +\command{patch} entiende esas diferencias como \emph{modificaciones} +para construir un fichero. Vea en la figura~\ref{ex:mq:diff} un +ejemplo sencillo de tales órdenes en acción. + +\begin{figure}[ht] + \interaction{mq.dodiff.diff} + \caption{Uso sencillo de las órdenes \command{diff} y \command{patch}} + \label{ex:mq:diff} +\end{figure} + +El tipo de fichero que \command{diff} genera (y que \command{patch} +toma como entrada) se llama un ``parche'' o un ``diff''; no hay +diferencia entre un parche y un diff. (Usaremos el término ``parche'', +dado que es el que más comunmente se usa.) + +Un parche puede comenzar con un texto arbitrario; la orden \command{patch} +ignora este texto, pero MQ lo usa como el mensaje de consignación +cuando se crean conjuntos de cambios. Para encontrar el inicio del +contenido de un parche, la orden \command{patch} busca la primera +línea que comience con la cadena ``\texttt{diff~-}''. + +MQ trabaja con diffs \emph{unificados} (\command{patch} acepta varios +formatos de diff adicionales, pero MQ no). Un diff unificado contiene +dos clases de encabezados. El \emph{encabezado de fichero} describe +el fichero que se está modificando; contiene el nombre del fichero a +modificar. Cuando \command{patch} ve un nuevo encabezado de fichero, +busca un fichero con ese nombre para modificarlo. + +Después del encabezaado vienen varios \emph{trozos}. Cada trozo +comienza con un encabezado; que identifica el rango de líneas del +fichero que el trozo debe modificar. Después del encabezado, un trozo +comienza y termina con unas pocas líneas (usualmente tres) de texto del +fichero que no han sido modificadas; las cuales llamamos el +\emph{contexto} del trozo. Si solamente hay una pequeña cantidad de +contexto entre trozos sucesivos, \command{diff} no imprime un nuevo +encabezado para el trozo, continua integrando los trozos, con unas +líneas de contexto entre las modificaciones. + +Cada línea de contexto comienza con un caracter de espacio. En el +trozo, si una línea comienza con ``\texttt{-}'' significa ``elimine +esta línea'', si la línea comienza con un ``\texttt{+}'' significa +``inserte esta línea''. Por ejemplo, una línea que se modifica se +representa con una línea eliminada y una línea insertada. + +Retomaremos aspectos más sutiles acerca de parches posteriormente (en +la sección~\ref{sec:mq:adv-patch}), pero en el momento usted ya +debería tener suficiente información para usar MQ. + +\section{Comenzar a usar Colas de Mercurial} +\label{sec:mq:start} + +Dado que MQ está implementado como una extensión, debe habilitarla +explícitamente antes de comenzar a usarla. (No necesita descargar +nada; MQ viene con la distribución estándar de Mercurial.) Para +habilitar MQ, edite su fichero \tildefile{.hgrc}, y añada las líneas +de la figura~\ref{ex:mq:config}. + +\begin{figure}[ht] + \begin{codesample4} + [extensions] + hgext.mq = + \end{codesample4} + \label{ex:mq:config} + \caption{Líneas a añadir en \tildefile{.hgrc} para habilitar la extensión MQ} +\end{figure} + +Cuando la extensión esté habilitada, aparecerán varios comandos. Para +verificar que la extensión está trabajando, puede usar \hgcmd{help} +para ver si la orden \hgxcmd{mq}{qinit} está disponible; vea un +ejemplo en la figura~\ref{ex:mq:enabled}. + +\begin{figure}[ht] + \interaction{mq.qinit-help.help} + \caption{Cómo verificar que MQ está habilitado} + \label{ex:mq:enabled} +\end{figure} + +Puede usar MQ en \emph{cualquier} repositorio de Mercurial, y sus +comandos solamente operarán con tal repositorio. Para comenzar, basta +con preparar el repositorio con la orden \hgxcmd{mq}{qinit} (ver la +figura~\ref{ex:mq:qinit}). Esta orden crea un directorio vacío +llamado \sdirname{.hg/patches}, donde MQ mantendrá sus metadatos. Como +otras ordenes de Mercurial, la orden \hgxcmd{mq}{qinit} no imprime +nada cuando es exitosa. + +\begin{figure}[ht] + \interaction{mq.tutorial.qinit} + \caption{Preparar un repositorio para usar MQ} + \label{ex:mq:qinit} +\end{figure} + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew} + \caption{Crear un nuevo parche} + \label{ex:mq:qnew} +\end{figure} + +\subsection{Crear un nuevo parche} + +Para comenzar a trabajar en un nuevo parche use la orden +\hgxcmd{mq}{qnew}. Esta orden recibe un argumento, el nombre del +parche a crear. MQ lo usará como el nombre del fichero en el +directorio \sdirname{.hg/patches}, como puede apreciarlo en la +figura~\ref{ex:mq:qnew}. + +También hay otros dos nuevos ficheros en el directorio +\sdirname{.hg/patches}: \sfilename{series} y \sfilename{status}. El +fichero \sfilename{series} lista todos los parches de los cuales MQ +tiene noticia para este repositorio, con un parche por línea. +Mercurial usa el fichero \sfilename{status} para mantener registros +interns; da seguimiento a todos los parches que MQ ha \emph{aplicado} +en el repositorio. + +\begin{note} + En ciertas ocasiones usted querrá editar el fichero + \sfilename{series} a mano; por ejemplo, cambiar el orden en que se + aplican ciertos parches. A pesar de esto, es una mala idea editar + manualmente el fichero \sfilename{status}, dado que es fácil + desorientar a MQ acerca de lo que está pasando. +\end{note} + +Una vez que haya creado un nuevo parche, puede editar los ficheros en +el directorio de trabajo, como lo haría usualmente. Toda las órdenes +que de a Mercurial, tales como \hgcmd{diff} y \hgcmd{annotate}, +trabajarán de la misma forma como lo han hecho antes. + +\subsection{Refrescar un parche} + +Cuando usted llega a un punto en el cual desea guardar su trabajo, use +la orden \hgxcmd{mq}{qrefresh} (figura~\ref{ex:mq:qnew}) para +actualizar el parche en el cual está trabajando. Esta orden almacena +los cambios que haya hecho al directorio actual de trabajo en su +parche, y almacena el conjunto de cambios correspondiente que contiene +los cambios. + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh} + \caption{Refrescar un parche} + \label{ex:mq:qrefresh} +\end{figure} + +Puede ejecutar la orden \hgxcmd{mq}{qrefresh} tan seguido como quiera, +y es una buena forma de ``colocar marcas'' a su trabajo. Refresque su +parche en momentos oportunos; intente un experimento; si el +experimento no funciona, Use \hgcmd{revert} sobre sus modificaciones +para volver al refresco anterior. + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh2} + \caption{Refrescar un parche muchas veces para acumular cambios} + \label{ex:mq:qrefresh2} +\end{figure} + +\subsection{Aplicar un parche tras otro y dar seguimiento} + +Cuando haya terminado de trabajar en un parche, o necesite trabajar en +otro, puede usar la orden \hgxcmd{mq}{qnew} para crear un nuevo +parche. Mercurial aplicará este parche sobre su parche anterior. +Para un ejemplo, ver la figura~\ref{ex:mq:qnew2}. Note que el parche +contiene los cambios en nuestro parche anterior como parte de su +contexto (lo verá más claramente en la salida de \hgcmd{annotate}). + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew2} + \caption{Aplicar un parche después del primero} + \label{ex:mq:qnew2} +\end{figure} + +Hasta ahora, con excepción de \hgxcmd{mq}{qnew} y +\hgxcmd{mq}{qrefresh}, hemos sido cuidadosos para aplicar únicamente +órdenes usuaales de Mercurial. De todas maneras, MQ ofrece muchos +comandos que son más sencillos de usar cuando esté pensando acerca de +parches, como se puede ver en la figura~\ref{ex:mq:qseries}: + +\begin{itemize} +\item La orden \hgxcmd{mq}{qseries} lista cada parche del cual MQ + tiene noticia en este repositorio, desde el más antiguo hasta el más + nuevo (El último \emph{creado}). +\item La orden \hgxcmd{mq}{qapplied} lista cada parche que MQ haya + \emph{aplicado} en este repositorio, de nuevo, desde el más antiguo + hasta el más nuevo (El aplicado más recientemente). +\end{itemize} + +\begin{figure}[ht] + \interaction{mq.tutorial.qseries} + \caption{Entender la pila de parches con \hgxcmd{mq}{qseries} y + \hgxcmd{mq}{qapplied}} + \label{ex:mq:qseries} +\end{figure} + +\subsection{Manipular la pila de parches} + +La discusión previa indicó que debe haber una diferencia entre los +parches ``conocidos'' y ``aplicados'', y efectivamente la hay. MQ +puede manejar un parche sin que este haya sido aplicado al +repositorio. + +Un parche \emph{aplicado} tiene su correspondiente conjunto de cambios +en el repositorio, y los efectos del parche y el conjunto de cambios +son visibles en el directorio de trabajo. Puede deshacer la +aplicación de un parche con la orden \hgxcmd{mq}{qpop}. MQ +\emph{sabe acerca de}, o maneja un parche sustraído, pero el parche ya +no tendrá un conjunto de cambios correspondientes en el repositorio, y +el directorio de trabajo no contendrá los cambios hechos por el +parche. La figura~\ref{fig:mq:stack} ilustra la diferencia entre +parches aplicados y seguidos. + +\begin{figure}[ht] + \centering + \grafix{mq-stack} + \caption{Parches aplicados y no aplicados en la pila de parches de MQ} + \label{fig:mq:stack} +\end{figure} + +Puede reaplicar un parche no aplicado o sustraído con la orden +\hgxcmd{mq}{qpush}. Esto crea un nuevo conjunto de cambios +correspondiente al parche, y los cambios del parche estarán presentes +de nuevo en el directorio de trabajo. Vea ejemplos de +\hgxcmd{mq}{qpop} y \hgxcmd{mq}{qpush} en acción en la +figura~\ref{ex:mq:qpop}. Vea que hemos sustraído uno o dos parches, +la salida de\hgxcmd{mq}{qseries} continúa igual, mientras que +\hgxcmd{mq}{qapplied} ha cambiado. + +\begin{figure}[ht] + \interaction{mq.tutorial.qpop} + \caption{Modificar la pila de parches aplicados} + \label{ex:mq:qpop} +\end{figure} + +\subsection{Introducir y sustraer muchos parches} + +Mientras que \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} operan sobre un +único parche cada vez, puede introducir y sustraer varios parches de +una vez. La opción \hgxopt{mq}{qpush}{-a} de \hgxcmd{mq}{qpush} +introduce todos los cambios que no hayan sido aplicados, mientras que +la opción \hgxopt{mq}{qpop}{-a} de \hgxcmd{mq}{qpop} sustrae todos los +cambios aplicados. (Vea la sección~\ref{sec:mq:perf} más adelante +en la cual se explican otras formas de de introducir y sustraer varios +cambios.) + +\begin{figure}[ht] + \interaction{mq.tutorial.qpush-a} + \caption{Pushing all unapplied patches} + \label{ex:mq:qpush-a} +\end{figure} + +\subsection{Medidas de seguridad y cómo saltarlas} + +Muchas órdenes MQ revisan el directorio de trabajo antes de hacer +cualquier cosa, y fallan si encuentran alguna modificación. Lo hacen +para garantizar que usted no pierda cambio alguno de los que haya +hecho, pero que no hayan sido incorporados en algún parche. La +figura~\ref{ex:mq:add} ilusta esto; la orden \hgxcmd{mq}{qnew} no +creará un nuevo parche si hay cambios notorios, causados en este caso +por aplicado la orden \hgcmd{add} a \filename{file3}. + +\begin{figure}[ht] + \interaction{mq.tutorial.add} + \caption{Crear un parche a la fuerza} + \label{ex:mq:add} +\end{figure} + +Las órdenes que revisan el directorio actual cuentan con una opción +``Se lo que estoy haciendo'', que siempre está nombrada como +\option{-f}. El significado exacto de \option{-f} depende de la +orden. Por ejemplo, \hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} +incorporarán cualquier cambio notorio en el nuevo parche que crea pero +\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} revertirá las modificaciones a +cualquier fichero que haya sido afectado por el parche que está siendo +sustraído. ¡Asegúrese de leer la documentación de la opción \option{-f} +de cada comando antes de usarla! + +\subsection{Trabajar con varios parches a la vez} + +La orden \hgxcmd{mq}{qrefresh} siempre refresca el \emph{último} +parche aplicado. Esto significa que usted puede suspender su trabajo +en un parche (refrescándolo), sustraerlo o introducirlo para lograr +que otro parche esté de último y trabajar en \emph{ese} parche por un +rato. + +A continuación un ejemplo que ilustra cómo puede usar esta habilidad. +Digamos que está desarrollando una nueva característica en dos +parches. El primero es un cambio en la parte fundamental de su +programa, y el segundo--sobre el primero---cambia la interfaz de +usuario para usar el código que ha añadido a la parte fundamental. Si +ve que hay un fallo en la parte fundamental mientras está trabajando +en el parche de UI\ndt{Interfaz de Usuario, User Interface en inglés}, es fácil arreglar la parte fundamental. +Simplemente use \hgxcmd{mq}{qrefresh} sobre el parche de la UI para +guardar los cambios de su trabajo en progreso, y use \hgxcmd{mq}{qpop} +para sacar sustraer el parche de la parte fundamental. Arregla el +fallo sobre la parte fundamental, aplique \hgxcmd{mq}{qrefresh} sobre +el parche fundamental, y aplique \hgxcmd{mq}{qpush} sobre el parche de +UI para continuar donde había quedado. + +\section{Más acerca de parches} +\label{sec:mq:adv-patch} + +MQ usa la orden GNU \command{patch} para aplicar los parches, por lo +tanto es útil conocer ciertos detalles de cómo trabaja +\command{patch}, y también acerca de los parches. + +\subsection{La cantidad de franjas} + +Si ve el encabezado de un parche, notará que la ruta al fichero tiene +un componente adicional al principio, que no está presente en la +ruta. Esta es una traza de cómo generaba anteriormente los parches la +gente (algunos aún lo hacen, pero es raro con las herramientas de +control de revisiones del actuales). + +Alicia desempaquetaría un comprimido, editaría sus ficheros, y querría +crear un parche. Por lo tanto ella renombraría su directorio de +trabajo, desempacaría el comprimido de nuevo (para lo cual necesitó el +renombramiento), y usaría las opciones \cmdopt{diff}{-r} y +\cmdopt{diff}{-N} de \command{diff} para generar recursivamente un +parche entre el directorio original y el modificado. El resultado +sería que el nombre del directorio original estaría al principio de +toda ruta en cada encabezado de fichero, y el nombre del directorio +modificado estaría al frente de la porción derecha de la ruta del +fichero. + +Como alguien que reciba un parche de Alicia en la red podría obtener +dos directorios, uno original y el otro modificado con exactamente los +mismos nombres, la orden \command{patch} tiene la opción +\cmdopt{patch}{-p} que indica la cantidad de componentes de la ruta +a eliminar cuando se vaya a aplicar el parche. Este número se +llama la \emph{cantidad de eliminaciones}. + +La opción con ``\texttt{-p1}'' significa ``elimine uno''. Si +\command{patch} ve un nombre de fichero \filename{foo/bar/baz} en el +encabezado del fichero, eliminará \filename{foo} y tratará de parchar +un fichero llamado \filename{bar/baz}. (Hablando estrictamente, la +cantidad de eliminaciones se refiere a la cantidad de \emph{separadores de + ruta} (y los componentes que vayan con ellos) a eliminar. Si el +contador es uno volverá \filename{foo/bar} en \filename{bar}, pero +\filename{/foo/bar} (note la barra extra) en \filename{foo/bar}.) + +La cantidad a eliminar``estándar'' para parches es uno; casi todos los +parches contienen un componente inicial de la ruta que necesita ser +eliminado. La orden \hgcmd{diff} de Mercurial genera nombres de ruta +de esta forma, y la orden \hgcmd{import} y MQ esperan parches que +tengan a uno como cuenta de eliminaciones. + +Si recibe un parche de alguien de quien desea adicionar adicionar a su +cola de parches, y el parche necesita una cuenta de eliminación que no +sea uno, no podrá aplicar \hgxcmd{mq}{qimport} en primera medida, +porque \hgxcmd{mq}{qimport} no tiene todavía una opción \texttt{-p} +option (ver~\bug{311}). Lo mejor que puede hacer es aplicar +\hgxcmd{mq}{qnew} por su cuenta, y después usar \cmdargs{patch}{-p\emph{N}} +para aplicar tal parche, seguido de \hgcmd{addremove} para tener en +cuenta cualquier fichero adicionado o eliminado por el parche, seguido +de \hgxcmd{mq}{qrefresh}. Esta complejidad puede ser innecesaria; +consulte~\bug{311} para más información. + +\subsection{Estrategias para aplicar parches} + +Cuando \command{patch} aplica un trozo, intenta varias estrategias +sucesivas que decrecen en precisión para intentar aplicarlo. Esta +técnica de pruebas y error aveces permite que un parche que fue +generado contra una versión anterior de un fichero, sea aplicada sobre +una versión más nueva del mismo. + +Primero \command{patch} intenta una correspondencia perfecta donde los +números de línea, el contexto y el texto a modificar deben coincidir +perfectamente. Si no lo logra, intenta encontrar una correspondencia +exacta del contexto, sin tener en cuenta el número de línea. Si es +exitoso, imprime una línea indicando que el trozo fue aplicado, pero a +un \emph{corrimiento} del número de línea original. + +Si falla la correspondencia por contexto, \command{patch} elimina la +primera y la última línea del contexto, e intenta una correspondencia +\emph{reducida} del contexto. Si el trozo con contexto reducido es +exitoso, imprime un mensaje indicando que aplicó el trozo con un +\emph{factor difuso} (el número después del factor difuso indica +cuántas líneas de contexto \command{patch} tuvo que eliminar antes de +aplicar el parche). + +Cuando ninguna de estas técnicas funciona, \command{patch} imprime un +mensaje indicando que el trozo en cuestión se desechó. Almacena los +trozos desechados (también llamados ``descartados'') en un fichero con +el mismo nombre, y la extensión \sfilename{.rej} añadida. También +almacena una copia igual al fichero original con la extensión +\sfilename{.orig}; la copia del fichero sin extensión contendrá +cualquier cambio hecho por los trozos que \emph{sí} se aplicaron sin +problema. Si usted tiene un parche que modifica \filename{foo} con +seis trozos, y uno de ellos falla al aplicarse, tendrá : un fichero +original \filename{foo.orig}, un fichero \filename{foo.rej} que +contiene el trozo, y \filename{foo}, que contiene los cambios que se +aplicaron por los cinco trozos exitosos. + +\subsection{Algunos detalles de la representación de parches} + +Hay ciertas cosas útiles por saber acerca de cómo trabaja +\command{patch} con los ficheros: +\begin{itemize} +\item Debería ser obvio que \command{patch} no puede manipular + ficheros binarios. +\item No se preocupa por el bit ejecutable; crea ficheros nuevos en + modo lectura, pero no ejecutable. +\item \command{patch} intenta eliminar un fichero como una diferencia + entre el fichero a eliminar y un fichero vacío. Y por lo tanto su + idea de ``Borré este fichero'' debería pensarse como ``toda línea de + este fichero fue eliminada'' en un parche. +\item Trata la adición de un fichero como un diff entre un fichero + vacío y el fichero a ser adicionado. Por lo tanto en un parche su + idea de ``Añadí este fichero'' se vería como ``toda línea de este + fichero fue añadida''. +\item Trata el renombramiento de un fichero como la eliminación del + nombre anterior y la adición del nuevo nombre. Esto significa que + los ficheros renombrados dejan un rastro grande en los parches. + (Tenga en cuenta que Mercurial no trata de inferir cuando los + ficheros han sido renombrados o copiados en un parche en este + momento.) +\item \command{patch} no puede representar ficheros vacíos, por lo + tanto no puede usar un parche para representar la noción ``Añadí + este fichero vacío al árbol''. +\end{itemize} +\subsection{Cuidado con los difusos} + +Cuando aplique un trozo con un corrimiento, o con un factor difuso, +aveces será taotalmente exitoso, tales técnicas inexactas dejan +claramente la posibilidad de corromper el fichero parchado. Los casos +más típicos involucran aplicar un parche dos veces o en un sitio +incorrecto del fichero. Si \command{patch} o \hgxcmd{mq}{qpush} llegan +a mencionar un corrimiento o un factor difuso, debería asegurarse que +los ficheros modificados estén correctos después del suceso. + +Casi siempre es buena idea refrescar un parche que fue aplicado con un +corrimiento o un factor difuso; refrescar el parche genera nueva +información de contexto que permitirá aplicarlo limpiamente. Digo +``casi siempre,'' no ``siempre'', puesto que en ciertas ocasiones +refrescar un parche lo hará fallar frente a una revisión diferente del +fichero. En algunos casos, como por ejemplo, cuando usted está +manteniendo un parche que debe estar encima de múltiples revisiones de +un árbol de fuentes, es aceptable tener un parche aplicado algo +difuso, siempre que haya verificado los resultados del proceso de +parchar. + +\subsection{Manejo de descartes} + +Si \hgxcmd{mq}{qpush} falla al aplicar un parche, mostrará un texto de +error y saldrá. Si ha dejado ficheros \sfilename{.rej}, es mejor +arreglar los trozos descartados antes de introducir parches +adicionales o hacer cualquier otra cosa. + +Si su parche \emph{solía} aplicarse limpiamente, y ya no lo hace +porque ha cambiado código subyacente en el cual se basa su parche, las +Colas de Mercurial pueden ayudar; consulte la sección~\ref{sec:mq:merge}. + +Desafortunadamente, no hay grandes técnicas para tratar los trozos +descartados. Casi siempre deberá consultar el fichero +\sfilename{.rej} y editar el fichero objetivo, aplicando los trozos +descartados a mano. + +Si es aventurero, Neil Brown, un hacker del núcleo Linux, escribió una +herramienta llamada \command{wiggle}~\cite{web:wiggle}, que es más +vigorosa que \command{patch} en su intento de hacer que se aplique un +parche. + +Otro hacker del nucleo Linux, Chris Mason (el autor de las Colas de +Mercurial), escribió una herramienta similar llamada +\command{mpatch}~\cite{web:mpatch}, que sigue una aproximación +sencilla para automatizar la aplicación de trozos descartados por +\command{patch}. La orden \command{mpatch} puede ayudar con cuatro +razones comunes por las cuales un parche ha sido descartado: + +\begin{itemize} +\item El contexto en la mitad de un trozo ha cambiado. +\item Un trozo ha perdido cierto contexto al principio o al final. +\item Un trozo largo podría aplicarse mejor---por completo o una + parte---si estaba cortado en trozos más pequeños. +\item Un trozo remueve líneas con contenido ligeramente diferente que + aquellas que están presentes en el fichero. +\end{itemize} + +Si usted usa \command{wiggle} o \command{mpatch}, debería ser +doblemente cuidadoso al revisar sus resultados cuando haya terminado. +De hecho, \command{mpatch} refuerza este método de revisar por partida +doble su salida, dejándolo a usted en un programa de fusión cuando la +herramienta haya terminado su trabajo, de tal forma que usted pueda +verificar lo que ha hecho y pueda terminar de aplicar cualquier fusión +faltante. + +\section{maximizar el rendimiento de MQ} +\label{sec:mq:perf} + +MQ es muy eficiente al tratar con una gran cantidad de parches. Corrí +unos experimentos de desempeño a mediados del 2006 para una charla que +dí en la conferencia EuroPython 2006~\cite{web:europython}. Empleé la +serie de parches para el núcleo Linux 2.6.17-mm1, que contaba con 1.738 +parches. Los apliqué sobre un repositorio del núcleo de Linux con +todas las 27.472 revisiones entre 2.6.12-rc2 y 2.6.17. + +En mi portátil antiguo y lento, logré aplicar +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} a los 1.738 parches en 3.5 +minutos, y \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} en 30 segundos. +(En un portátil más nuevo, el tiempo para introducir todos los +parches, se logró en menos de dos minutos.) Apliqué +\hgxcmd{mq}{qrefresh} sobre uno de los parches más grandes (que hizo +22.779 líneas de cambios en 287 ficheros) en 6,6 segundos. + +Claramente, MQ funciona adecuadamente en árboles grandes, y además hay +unos trucos que pueden emplearse para obtener el máximo desempeño. + +En primer lugar, trate de hacer ``en lote'' las operaciones. Cada vez +que ejecute \hgxcmd{mq}{qpush} o \hgxcmd{mq}{qpop}, tales órdenes +revisan el directorio de trabajo para asegurarse de que usted no ha +hecho cambios y ha olvidado ejecutar \hgxcmd{mq}{qrefresh}. En un +árbol pequeño, el tiempo de esta revisión puede ser mínimo, Pero en +un árbol mediano (con decenas de miles de ficheros), puede tomar un +segundo o más. + +Las órdenes \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} le permiten +introducir o sustraer varios parches en una operación. Puede +identificar el ``parche destino'' que desee. Cuando aplique +\hgxcmd{mq}{qpush} con un destino, introducirá tantos parches como sea +necesario hasta que el especificado esté en el tope de la pila. +Cuando emplee \hgxcmd{mq}{qpop} con un destino, MQ sustraerá parches +hasta que el parche destino esté en el tope. + +Puede identificar un parche destino con el nombre del parche o con el +número. Si se refiere al número, los parches se contarán desde cero; +esto significa que el primer parche es cero, el segundo es uno y así +sucesivamente. + +\section{Actualiar los parches cuando el código cambia} +\label{sec:mq:merge} + +Es común contar con una pila de parches sobre un repositorio que usted +no modifica directamente. Si está trabajando en cambios de código de +otros, o en una característica que tarda bastante en desarrollarse +comparada con la tasa de cambio del código sobre la cual se está +trabajando, necesitará sincronizarse con el código, y ajustar +cualquier trozo en sus parches que ya no estén al día. A esto se le +llama hacer \emph{rebase} a su serie de parches. + +La vía más sencilla de hacerlo es con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} +sobre sus parches, después hacer \hgcmd{pull} de los cambios en el +repositorio, y finalmente hacer +\hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} con sus parches de nuevo. MQ +dejará de de introducir parches siempre que llegue a un parche que no se pueda +aplicar debido a un conflicto, permitiéndole a usted arreglarlo, +aplicar \hgxcmd{mq}{qrefresh} al parche afectado y continuar +introduciendo hasta que haya arreglado la pila completa. + +Esta aproximación es sencilla y funciona bien si no espera cambios en +el código original que afecte en gran medida los parches que usted +esté aplicando. Si su pila de parches toca código que es modificado +frecuentemente o de forma invasiva sobre el código subyacente, +arreglar trozos manualmente se vuelve desgastante. + +Es posible automatizar de forma parcial el proceso de rebase. Si sus +parches se aplican limpiamente sobre algunas revisiones del +repositorio subyacente, MQ puede usar esta información para ayudarle a +a resolver conflictos entre sus parches y una revisión distinta. + +El proceso resulta un poco complejo: +\begin{enumerate} +\item Para comenzar, haga \hgcmdargs{qpush}{-a} sobre todos los + parches que usted sepa se aplican limpiamente. +\item Guarde una copia de seguridad de su directorio de parches con + \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}}. + Esto imprime el nombre del directorio en el cual se han guardado los + parches. Guardará los parches en un directorio llamado + \sdirname{.hg/patches.\emph{N}}, donde \texttt{\emph{N}} es un + entero pequeño. También consigna un ``conjunto de cambios de + seguridad'' sobre sus parches aplicados; esto es para mantener el + histórico, y guarda los estados de los ficheros \sfilename{series} + y \sfilename{status}. +\item Use \hgcmd{pull} para traer los nuevos cambios en el repositorio + subyacente. (No ejecute \hgcmdargs{pull}{-u}; vea más adelante por qué.) +\item Actualice a la nueva revisión punta con + \hgcmdargs{update}{\hgopt{update}{-C}} para sobreescribir los + parches que haya introducido. +\item Fusione todos los parches con \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} + \hgxopt{mq}{qpush}{-a}}. La opción \hgxopt{mq}{qpush}{-m} de \hgxcmd{mq}{qpush} + le indica a MQ que haga una fusión que involucra tres fuentes si el + parche falla al aplicarse. +\end{enumerate} + +Durante el \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}}, cada parche en +el fichero \sfilename{series} se aplica normalmente. Si un parche se +aplica difusamente o se niea a aplicarse, MQ consulta la cola que +usted guardó con \hgxcmd{mq}{qsave}, y aplica una fusión de tres con +el correspondiente conjunto de cambios. Esta fusión usa la maquinaria +de Mercurial, por lo tanto puede mostrar una herramienta de fusión GUI +para ayudarle a resolver los problemas. + +Cuando termine de resolver los efectos de un parche, MQ refrescará su +parche basado en el resultado de la fusión. + +Al final de este proceso, su repositorio tendrá una cabeza extra de la +antigua cola de parches, y una copia de la cola de parches anterio +estará en \sdirname{.hg/patches.\emph{N}}. Puede eliminar la cabeza +extra con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} +o \hgcmd{strip}. Puede eliminar \sdirname{.hg/patches.\emph{N}} una +vez que esté seguro de que no lo necesita más como copia de seguridad. + +\section{Identificar parches} + +Las órdenes de MQ le permiten trabajar refiriéndose al nombre del +parche o al número. Es obvio hacerlo por el nombre; por ejemplo se +pasa el nombre \filename{foo.patch} a \hgxcmd{mq}{qpush}, que +introducirá los parches hasta que \filename{foo.patch} se aplique. + +Para hacerlo más corto, puede referirse a un parche con un nombre y un +corrimiento de número; por ejemplo, \texttt{foo.patch-2} significa +``dos parches antes de \texttt{foo.patch}'', mientras que +\texttt{bar.patch+4} significa ``cuatro parches después de \texttt{bar.patch}''. + +Referirse a un parche por su índice no es muy diferente. El primer +parche que se imprime en la salida de \hgxcmd{mq}{qseries} es el +parche cero (si, es el primero en los sistemas que comienzan su conteo +en cero); el segundo parche es uno y así sucesivamente. + +MQ facilita el trabajo cuando está usando órdenes normales de +Mercurial. Cada comando que acepte Identificadores de conjuntos de +cambios también aceptará el nombre de un parche aplicado. MQ aumenta +las etiquetas normalmente en el repositorio con un distintivo para cada +parche aplicado. Adicionalmente, las etiquetas especiales \index{tags!special tag + names!\texttt{qbase}}\texttt{qbase} y \index{tags!special tag + names!\texttt{qtip}}\texttt{qtip} identifican los parches +``primero'' y último, respectivamente. + +Junto con las capacidades de Mercurial para etiquetar, estas adiciones +hacen que trabajar con parches sea muy sencillo. +\begin{itemize} +\item ¿Desea enviar una bomba de parches a una lista de correo con los + últimos cambios que ha hecho? + \begin{codesample4} + hg email qbase:qtip + \end{codesample4} + (¿No sabe qué es una ``bomba de parches''? Consulte la + sección~\ref{sec:hgext:patchbomb}.) +\item ¿Desea ver todos los parches desde que se aplicó + \texttt{foo.patch} sobre los ficheros de un subdirectorio en su + árbol? + \begin{codesample4} + hg log -r foo.patch:qtip \emph{subdir} + \end{codesample4} +\end{itemize} + +Dado que MQ nombra los parches disponibles al resto de Mercurial con +su maquinaria de etiquetas interna, usted no necesita teclear el +nombre completo de un parche cuando desea identificarlo por su nombre. + +\begin{figure}[ht] + \interaction{mq.id.output} + \caption{Uso de las características de etiquetamiento al trabajar + con MQ} + \label{ex:mq:id} +\end{figure} + +Otra consecuencia deseable al representar los nombres de parches como +etiquetas es que cuando ejecute la orden \hgcmd{log}, desplegará el +nombre del parche como una etiqueta, usualmente con la salida normal. +Esto facilita distinguir visualmente los parches aplicados de las +revisiones ``normales''. La figura~\ref{ex:mq:id} muestra algunos +comandos usuales de Mercurial al trabajar con parches. + +\section{Otra información útil} + +Hay una cantidad de aspectos que hacen que el uso de MQ no representen +secciones en sí mismas, pero de los cuales es bueno estar +enterado. Los presentamos en aquí: + +\begin{itemize} +\item Usualmente cuando hace \hgxcmd{mq}{qpop} a un parche y vuelve a + hacerle \hgxcmd{mq}{qpush}, el conjunto de cambios que representa el + parche después de introducir/sustraer tendrá una \emph{identidad + distinta} que aquella que representaba el conjunto de cambios + anteriormente. Consulte la secctión~\ref{sec:mqref:cmd:qpush} para + obtener información del por qué de esto. +\item No es una buena idea aplicar \hgcmd{merge} de cambios de otra + rama con un conjunto de cambios de parches, por lo menos si desea + mantener la ``información de parches'' de ese conjunto de cambios y + los conjuntos de cambios que se encuentran por debajo en la pila de + parches. Si intenta hacerlo, parecerá que ha sido exitoso, pero MQ + se confundirá. +\end{itemize} + +\section{Administrar parches en un repositorio} +\label{sec:mq:repo} + +Dado que el directorio \sdirname{.hg/patches} de MQ reside fuera del +repositorio de trabajo de Mercurial, el repositorio ``subyacente'' de +Mercurial no sabe nada acerca de la administración o presencia de +parches. + +Esto presenta la interesante posibilidad de administrar los contenidos +del directorio de parches como un repositorio de Mercurial por su +cuenta. Puede ser una forma útil de trabajar. Por ejemplo, puede +trabajar en un parche por un rato, hacerle \hgxcmd{mq}{qrefresh} y +después hacer \hgcmd{commit} al estado actual del parche. Esto le +permite ``devolverse'' a esa versión del parche posteriormente. + +Puede también compartir diferentes versiones de la misma pila de +parches entre varios repositorios subyacentes. Uso esto cuando estoy +desarrollando una característica del núcleo Linux. Tengo una copia +original de las fuentes del núcleo para varias arquitecturas, y cloné +un rpositorio en cada una que contiene los parches en los cuales +estoy trabajando. Cuando quiero probar un cambio en una arquitectura +diferente, introduzco mis parches actuales al repositorio de parches +asociado con el árbol del kernel, sustraigo e introduzco todos mis +parches, armo y pruebo el núcleo. + +Llevar los parches en un repositorio permite que varios +desarrolladores puedan trabajar en la misma serie de parches sin +sobreponerse, todo sobre la fuente base subyacente que pueden o no +controlar. + +\subsection{Soporte de MQ para repositorios de parches} + +MQ le ayuda a trabajar con el directorio \sdirname{.hg/patches} como +un repositorio; cuando usted prepara un repositorio para trabajar con +parches usando \hgxcmd{mq}{qinit}, puede pasarle la opción +\hgxopt{mq}{qinit}{-c} para que se cree el directorio +\sdirname{.hg/patches} como un repositorio de Mercurial. + +\begin{note} + Si olvida usar la opción \hgxopt{mq}{qinit}{-c} option, puede ir al + directorio \sdirname{.hg/patches} en cualquier momento y ejecutar + \hgcmd{init}. No olvide añadir una entrada en el fichero + \sfilename{status} del fichero \sfilename{.hgignore}, a pesar de que + (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} hace estodo de forma + automática para usted); usted \emph{seguro} no quiere administrar el + fichero \sfilename{status}. +\end{note} + +MQ nota convenientemente que el directorio \dirname{.hg/patches} +es un repositorio, hará \hgcmd{add} automáticamente a cada parche que +usted cree e importe. + +MQ provee una orden corta, \hgxcmd{mq}{qcommit}, que ejecuta +\hgcmd{commit} en el directorio \sdirname{.hg/patches}. Lo que ahorra +tecleo recurrente. + +Finalmente, para administrar convenientemente el directorio de +parches, puede definir el alias \command{mq} en sistemas Unix. Por +ejemplo, en sistemas Linux con el intérprete \command{bash}, puede +incluir el siguiente recorte de código\ndt{snippet} en su fichero +\tildefile{.bashrc}. + +\begin{codesample2} + alias mq=`hg -R \$(hg root)/.hg/patches' +\end{codesample2} + +Puede aplicar las órdenes de la forma \cmdargs{mq}{pull} al +repositorio principal. + +\subsection{Detalles a tener en cuenta} + +El soporte de MQ para trabajar con un repositorio de parches es +limitado en ciertos aspectos: + +MQ no puede detectar automáticamente los cambios que haga al +directorio de parches. Si aplica \hgcmd{pull}, edita manualmente, o +hace \hgcmd{update} a los parches o el fichero \sfilename{series}, +tendrá que aplicar \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} y después +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} en el repositorio subyacente +para que los cambios se reflejen allí. Si olvida hacerlo, puede +confundir a MQ en cuanto a qué parches se han aplicado. + +\section{Otras herramientas para trabajar con parches} +\label{sec:mq:tools} + +Cuando haya trabajado por cierto tiempo con parches, deseará +herramientas que le ayuden a entender y manipular los parches con los +que esté tratando. + +La orden \command{diffstat}~\cite{web:diffstat} genera un histograma +de modificaciones hechas a cada fichero en un parche. Provee una +interesante forma de ``dar un vistazo'' al parche---qué ficheros +afecta, y cuántos cambios introduce a cada fichero y en total. (Me ha +parecido interesante usar la opción \cmdopt{diffstat}{-p} de +\command{diffstat}, puesto que de otra forma intentará hacer cosas +inteligentes con prefijos de ficheros que terminan confundiéndome.) + +\begin{figure}[ht] + \interaction{mq.tools.tools} + \caption{Las órdenes \command{diffstat}, \command{filterdiff}, y \command{lsdiff}} + \label{ex:mq:tools} +\end{figure} + +El paquete \package{patchutils}~\cite{web:patchutils} es +invaluable. Provee un conjunto de pequeñas utilidades que siguen la +``filosofía Unix''; cada una hace una cosa muy bien hecha a un +parche. La orden \package{patchutils} que más uso es +\command{filterdiff}, que extrae subconjuntos de un fichero de +parche. Por ejemplo, dado un parche que modifica centenas de ficheros +en docenas de directorios, una única invocación de +\command{filterdiff} puede generear un parche más pequeño que +solamente toca aquellos ficheros con un patrón. Puede ver otro +ejemplo en la sección~\ref{mq-collab:tips:interdiff}. + +\section{Buenas prácticas de trabajo con parches} + +En caso de que esté trabajando en una serie de parches para enviar a +un proyecto de software libre o de fuentes abiertas, o en una serie +que desea tratar como un conjunto de cambios regular, cuando haya +terminado, puede usar técnicas sencillas para mantener su trabajo bien +organizado. + +De nombres descriptivos a sus parches. Un buen nombre para un parche +podría ser \filename{rework-device-alloc.patch}, porque da de forma +inmediata una pista del propósito del parche. Los nombres largos no +deben ser un problema; no los estará tecleando repetidamente, pero +\emph{estará} ejecutando regularmente órdenes como +\hgxcmd{mq}{qapplied} y \hgxcmd{mq}{qtop}. Los nombres adecuados son +especialmente importantes cuando tiene bastantes parches con los +cuales trabajar, o si está trabajando en diferentes tareas y sus +parches toman solamente una porción de su atención. + +Tenga en cuenta en qué parche está trabajando. Use la orden \hgxcmd{mq}{qtop} +para dar un vistazo al texto de sus parches frecuentemente---por +ejemplo, use \hgcmdargs{tip}{\hgopt{tip}{-p}})---para asegurarse en +dónde está ubicado. En distintas oportunidades me sucedió que apliqué +\hgxcmd{mq}{qrefresh} a un parche distinto al que deseaba hacerlo, y +usualmente es complejo migrar los cambios al parche correcto después +de haberlo hecho mal. + +Por este motivo, vale la pena invertir ese poco tiempo para aprender +cómo usar otras herramientas que describí en la +sección~\ref{sec:mq:tools}, particularmente \command{diffstat} y +\command{filterdiff}. La primera le dará una idea de qué cambios está +haciendo su parche, mientras que la segunda permite seleccionar trozos +de un parche para colocarlos en otro. + +\section{Recetas de MQ} + +\subsection{Administrar parches ``triviales''} + +Puesto que colocar ficheros en un repositorio de Mercurial es tan +sencillo, tiene bastante sentido administrar parches de esta forma +incluso si desea hacer algunos cambios al paquete de ficheros que +descargó. + +Para comenzar a descargar y desempaqueter un paquete de ficheros, y +volverlo en un repositorio de Mercurial: +\interaction{mq.tarball.download} + +Continue creando una pila de parches y haga sus cambios. +\interaction{mq.tarball.qinit} + +Digamos que pasan unas semanas o meses, y el autor del paquete libera +una nueva versión. Primero se traen sus cambios al repositorio. +\interaction{mq.tarball.newsource} +La porción que comienza con \hgcmd{locate} mostrada más arriba, borra +todos los ficheros en el directorio de trabajo, así que la opción +\hgopt{commit}{--addremove} de \hgcmd{commit} puede indicar qué +ficheros se han eliminado en la nueva versión del árbol de fuentes. + +Finalmente, puede aplicar sus parches encima del nuevo árbol de fuentes +\interaction{mq.tarball.repush} + +\subsection{Combinar parches completos} +\label{sec:mq:combine} + +MQ provee la orden \hgxcmd{mq}{qfold} que le permite combinar parches +enteros. Se ``integran''\ndt{fold} los parches que usted nombre, en +el orden que especifique, en el último parche aplicado, y concatena +sus descripciones al final de su descripción. Deberá sustraer los +cambios para poder integrarlos. + +El orden en el que integre los parches importa. Si el parche +últimamente aplicado es \texttt{foo}, e integra \hgxcmd{mq}{qfold} \texttt{bar} y +\texttt{quux} en él, terminará con un parche que tiene el mismo efecto +que si hubiera aplicado primero \texttt{foo}, y después \texttt{bar}, +seguido de \texttt{quux}. + +\subsection{Fusionar una porción de un parche dentro de otro} + +Fusionar \emph{partes} de un parche dentro de otro es más complejo que +combinar completamente dos parches. + +Si desea mover cambios de ficheros completos, puede usar las opciones +\command{filterdiff}'s \cmdopt{filterdiff}{-i} y +\cmdopt{filterdiff}{-x} para elegir las modificaciones remover de un +parche, concatenar su salida al final del parche donde desea +fusionarlo. Usualmente no necesitará modificar el parche del cuál ha +fusionado los cambios. En cambio, MQ reportará que hay unos trozos +que se han desechado cuando usted aplique \hgxcmd{mq}{qpush} (de los +trozos que haya movido al otro parche), y puede sencillamente aplicar +\hgxcmd{mq}{qrefresh} para eliminar los trozos replicados. + +Si tiene un parche que tiene varios trozos que modifican un fichero, y +desea mover solamente unos de ellos, el trabajo es un poco más +enredado, pero puede automatizarlo parcialmente. Use +\cmdargs{lsdiff}{-nvv} para imprimir algunos metadatos del parche. +\interaction{mq.tools.lsdiff} + +Esta orden imprime tres clases diferentes de números: +\begin{itemize} +\item (en la primera columna) un \emph{número de fichero} para + identificar cada fichero modificado en el parche; +\item (En la siguiente línea, indentado) el número de línea dentro de + un fichero modificado donde comienza el trozo; y +\item (en la misma línea) un \emph{número de trozo} que identifica el + trozo. +\end{itemize} + +Tendrá que hacer una inspección visual, y leer el parche para +identificar los números de fichero y trozo que desea, pero puede pasar +posteriormente a las opciones \cmdopt{filterdiff}{--files} y +\cmdopt{filterdiff}{--hunks} de \command{filterdiff}, para seleccionar +exactamente el fichero y el trozo que desea extraer. + +Cuando tenga el trozo, puede concatenarlo al final de su parche +objetivo y continuar como en la sección~\ref{sec:mq:combine}. + +\section{Diferencias entre quilt y MQ} + +Si le es familiar quilt, MQ provee un conjunto similar de órdenes. Hay +algunas diferencias en cómo funcionan. + +Debe haber notado que la mayoría de comandos de quilt tienen su +contraparte en MQ, que simplemente comienzan con ``\texttt{q}''. Las +excepciones son las órdenes \texttt{add} y \texttt{remove} de quilt, +que realmente son las órdenes \hgcmd{add} y \hgcmd{remove} de +Mercurial. No hay un equivalente en MQ para la orden +\texttt{edit} de quilt. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/note.png Binary file es/note.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 es/preface.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/preface.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,74 @@ +\chapter*{Prefacio} +\addcontentsline{toc}{chapter}{Prefacio} +\label{chap:preface} + +El control distribuido de revisiones es un territorio relativamente +nuevo, y ha crecido hasta ahora +% TODO el original dice "due to", que sería "debido", pero creo que "gracias +% a" queda mejor +gracias a a la voluntad que tiene la gente de salir y explorar +territorios desconocidos. +% TODO revisar la frase anterior. me tomé muchas licencias para +% traducirla + +Estoy escribiendo este libro acerca de control de revisiones +distribuido porque creo que es un tema importante que merece una guía +de campo. Escogí escribir acerca de Mercurial porque es la herramienta +%TODO puse explorar en vez de aprender, you be the judge dear reviewer ;) +más fácil para explorar el terreno, y sin embargo escala a las +demandas de retadores ambientes reales donde muchas otras herramientas +de control de revisiones fallan. + +\section{Este libro es un trabajo en progreso} +Estoy liberando este libro mientras lo sigo escribiendo, con la +esperanza de que pueda ser útil a otros. También espero que los +lectores contribuirán como consideren adecuado. + +\section{Acerca de los ejemplos en este libro} +Este libro toma un enfoque inusual hacia las muestras de código. Cada +ejemplo está ``en directo''---cada uno es realmente el resultado de un +% TODO shell script +script de shell que ejecuta los comandos de Mercurial que usted ve. +Cada vez que una copia del libro es construida desde su código fuente, +% TODO scripts +todos los scripts de ejemplo son ejecutados automáticamente, y sus +resultados actuales son comparados contra los resultados esperados. + +La ventaja de este enfoque es que los ejemplos siempre son precisos; +ellos describen \emph{exactamente} el comportamiento de la versión de +Mercurial que es mencionada en la portada del libro. Si yo actualizo +la versión de Mercurial que estoy documentando, y la salida de algún +comando cambia, la construcción falla. + +Hay una pequeña desventaja de este enfoque, que las fechas y horas que +usted verá en los ejemplos tienden a estar ``aplastadas'' juntas de una +forma que no sería posible si los mismos comandos fueran escritos por +un humano. Donde un humano puede emitir no más de un comando cada +pocos segundos, con cualquier marca de tiempo resultante +correspondientemente separada, mis scripts automatizados de ejemplos +ejecutan muchos comandos en un segundo. + +% TODO commit +Como un ejemplo de esto, varios commits consecutivos en un ejemplo +pueden aparecer como habiendo ocurrido durante el mismo segundo. Usted +puede ver esto en el ejemplo \hgext{bisect} en la +sección~\ref{sec:undo:bisect}, por ejemplo. + +Así que cuando usted lea los ejemplos, no le dé mucha importancia a +las fechas o horas que vea en las salidas de los comandos. Pero +\emph{tenga} confianza en que el comportamiento que está viendo es +consistente y reproducible. + +\section{Colofón---este libro es Libre} +Este libro está licenciado bajo la Licencia de Publicación Abierta, y +es producido en su totalidad usando herramientas de Software Libre. Es +compuesto con \LaTeX{}; las ilustraciones son dibujadas y generadas +con \href{http://www.inkscape.org/}{Inkscape}. + +El código fuente completo para este libro es publicado como un +repositorio Mercurial, en \url{http://hg.serpentine.com/mercurial/book}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/revlog.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + Segundo padre + 32bf9a5f22c0 + + + + Hash de revisión + 34b8b7a15ea1 + + + + ... + Datos de Revisión (delta o snapshot) + + + + + + + Primer padre + 000000000000 + + + + Segundo padre + 000000000000 + + + + + Hash de revisión + ff9dc8bc2a8b + + + + ... + Datos de revisión (delta o snapshot) + + + + + + + Primer padre + 34b8b7a15ea1 + + + + Segundo padre + 000000000000 + + + + Hash de revisión + 1b67dc96f27a + + + + ... + Datos de revisión (delta o snapshot) + + + + + + + + Primer padre + ff9dc8bc2a8b + + + + Segundo padre + 000000000000 + + + + Hash de revisión + 5b80c922ebdd + + + + ... + Datos de revisión (delta o snapshot) + + + + + + + Primer padre + ecacb6b4c9fd + + + + Segundo padre + 000000000000 + + + + Hash de revisión + 32bf9a5f22c0 + + + + ... + Datos de revisión (delta o snapshot) + + + + + + Primer padre + ff9dc8bc2a8b + + + + Segundo padre + 000000000000 + + + + Hash de revisión + ecacb6b4c9fd + + + + ... + Datos de revisión (delta o snapshot) + + + + + + + Revisión principal(sin hijos) + Revisión de fusión(dos padres) + Ramas(dos revisiones,mismo padre) + + + Primera revisión(ambos padres nulos) + + Primer padre + 5b80c922ebdd + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 es/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/snapshot.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,212 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + Índice, rev 7 + + Índice de bitácora de revisiones (archivo .i) + Datos de Bitacora de revisiones (archivo .d) + + + Snapshot, rev 4 + + Delta, rev 4 a 5 + + Delta, rev 5 a 6 + + Delta, rev 6 a 7 + + Delta, rev 2 a 3 + + diff -r 5981a0f7540a -r 019040fbf5f5 es/srcinstall.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/srcinstall.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,55 @@ +\chapter{Instalar Mercurial desde las fuentes} +\label{chap:srcinstall} + +\section{En un sistema tipo Unix} +\label{sec:srcinstall:unixlike} + +Si usa un sistema tipo Unix que tiene una versión suficientemente +reciente de Python (2.3~o superior) disponible, es fácil instalar +Mercurial desde las fuentes. +\begin{enumerate} +\item Descargue un paquete fuente reciente de + \url{http://www.selenic.com/mercurial/download}. +\item Descomprímalo: + \begin{codesample4} + gzip -dc mercurial-\emph{version}.tar.gz | tar xf - + \end{codesample4} +\item Vaya al directorio fuente y ejecute el guión de instalación. + Esto armará Mercurial y lo instalará en su directorio casa: + \begin{codesample4} + cd mercurial-\emph{version} + python setup.py install --force --home=\$HOME + \end{codesample4} +\end{enumerate} +Cuando termine la instalación, Mercurial estará en el subdirectorio +\texttt{bin} de su directorio casa. No olvide asegurarse de que este +directorio esté presente en el camino de búsqueda de su intérprete de +órdenes. + +Probablemente necesitará establecer la variable de ambiente +\envar{PYTHONPATH} de tal forma que los ejecutables de Mercurial +puedan encontrar el resto de los paquetes de Mercurial. Por ejemplo, +en mi portátil, la establecía a \texttt{/home/bos/lib/python}. La +ruta exacta que usted use dependerá de como ha sido construído Python +en su sistema, pero debería ser fácil deducirla. Si no está seguro, +mire lo que haya mostrado el script en el paso anterior, y vea dónde +se instalaron los contenidos del directorio \texttt{mercurial} se +instalaron. + +\section{En Windows} + +Armar e instalar Mercurial en Windows requiere una variedad de +herramientas, cierta suficiencia técnica y paciencia considerable. +Personalmente, \emph{no le recomiendo} hacerlo si es un ``usuario +casual''. A menos que intente hacer hacks a Mercurial, le recomiendo +que mejor use un paquete binario. + +Si está decidido a construir Mercurial desde las fuentes en Windows, +siga el ``camino difícil'' indicado en el wiki de Mercurial en +\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall}, +y espere que el proceso sea realmente un trabajo duro. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/template.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/template.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,497 @@ +\chapter{Personalizar los mensajes de Mercurial} +\label{chap:template} + +Mercurial provee un poderoso mecanismo que permite controlar como +despliega la información. El mecanismo se basa en plantillas. Puede +usar plantillas para generar salida específica para una orden +particular o para especificar la visualización completa de la interfaz +web embebida. + +\section{Usar estilos que vienen con Mercurial} +\label{sec:style} + +Hay ciertos estilos listos que vienen con Mercurial. Un estilo es +simplemente una plantilla predeterminada que alguien escribió e +instaló en un sitio en el cual Mercurial puede encontrarla. + +Antes de dar un vistazo a los estilos que trae Mercurial, revisemos su +salida usual. + +\interaction{template.simple.normal} + +Es en cierta medida informativa, pero ocupa mucho espacio---cinco +líneas de salida por cada conjunto de cambios. El estilo +\texttt{compact} lo reduce a tres líneas, presentadas de forma +suscinta. + +\interaction{template.simple.compact} + +El estilo de la \texttt{bitácora de cambios} vislumbra el poder +expresivo del sistema de plantillas de Mercurial. Este estilo busca +seguir los estándares de bitácora de cambios del proyecto +GNU\cite{web:changelog}. + +\interaction{template.simple.changelog} + +No es una sorpresa que el estilo predeterminado de Mercurial se llame +\texttt{default}\ndt{predeterminado}. + +\subsection{Especificar un estilo predeterminado} + +Puede modificar el estilo de presentación que Mercurial usará para +toda orden vía el fichero \hgrc\, indicando el estilo que prefiere +usar. + +\begin{codesample2} + [ui] + style = compact +\end{codesample2} + +Si escribe un estilo, puede usarlo bien sea proveyendo la ruta a su +fichero de estilo o copiando su fichero de estilo a un lugar en el +cual Mercurial pueda encontrarlo (típicamente el subdirectorio +\texttt{templates} de su directorio de instalación de Mercurial). + +\section{Órdenes que soportan estilos y plantillas} + +Todas las órdenes de Mercurial``relacionadas con \texttt{log}'' le +permiten usar estilos y plantillas: \hgcmd{incoming}, \hgcmd{log}, +\hgcmd{outgoing} y \hgcmd{tip}. + +Al momento de la escritura del manual estas son las únicas órdenes que +soportan estilos y plantillas. Dado que son las órdenes más +importantes que necesitan personalización, no ha habido muchas +solicitudes desde la comunidad de usuarios de Mercurial para añadir +soporte de plantillas y estilos a otras órdenes. + +\section{Cuestiones básicas de plantillas} + +Una plantilla de Mercurial es sencillamente una pieza de texto. +Cierta porción nunca cambia, otras partes se \emph{expanden}, o +reemplazan con texto nuevo cuando es necesario. + +Antes de continuar, veamos de nuevo un ejemplo sencillo de la salida +usual de Mercurial: + +\interaction{template.simple.normal} + +Ahora, ejecutemos la misma orden, pero usemos una plantilla para +modificar su salida: + +\interaction{template.simple.simplest} + +El ejemplo anterior ilustra la plantilla más sencilla posible; es +solamente una porción estática de código que se imprime una vez por +cada conjunto de cambios. La opción \hgopt{log}{--template} de la +orden \hgcmd{log} indica a Mercurial usar el texto dado como la +plantilla cuando se imprime cada conjunto de cambios. + +Observe que la cadena de plantilla anterior termina con el texto +``\Verb+\n+''. Es una \emph{secuencia de control}, que le indica a +Mercurial imprimira una nueva línea al final de cada objeto de la +plantilla. Si omite esta nueva línea, Mercurial colocará cada pieza +de salida seguida. Si desea más detalles acerca de secuencias de +control, vea la sección~\ref{sec:template:escape}. + +Una plantilla que imprime una cadena fija de texto siempre no es muy +útil; intentemos algo un poco más complejo. + +\interaction{template.simple.simplesub} + +Como puede ver, la cadena ``\Verb+{desc}+'' en la plantilla ha sido +reemplazada en la salida con la descricipción de cada conjunto de +cambios. Cada vez que Mercurial encuentra texto encerrado entre +corchetes (``\texttt{\{}'' y ``\texttt{\}}''), intentará reemplazar los +corchetes y el texto con la expansión de lo que sea está adentro. +Para imprimir un corchete de forma literal, debe escaparlo, como se +describe en la sección~\ref{sec:template:escape}. + +\section{Palabras claves más comunes en las plantillas} +\label{sec:template:keyword} + +Puede empezar a escribir plantillas sencillas rápidamente con las +palabras claves descritas a continuación: + +\begin{itemize} +\item[\tplkword{author}] Cadena. El autor NO modificado del conjunto + de cambios. +\item[\tplkword{branches}] Cadena. El nombre de la rama en la cual se + consignó el conjunto de cambios. Será vacía si el nombre de la rama es + \texttt{default}. +\item[\tplkword{date}] Información de fecha. La fecha en la cual se + consignó el conjunto de cambios. \emph{No} es legible por un + humano, debe pasarla por un firltro que la desplegará + apropiadamente. En la sección~\ref{sec:template:filter} hay más + detalles acerca de filtros. La fecha se expresa como un par de + números. El primer número corresponde a una marca de tiempo UNIX + UTC (segundos desde el primero de enero de 1970); la segunda es el + corrimiento horario de la zona horaria del UTC en la cual se encontraba + quien hizo la consignación, en segundos. +\item[\tplkword{desc}] Cadena. La descripción en texto del conjunto + de cambios. +\item[\tplkword{files}] Lista de cadenas. Todos los ficheros + modificados, adicionados o eliminados por este conjunto de cambios. +\item[\tplkword{file\_adds}] Lista de cadenas. Ficheros adicionados + por este conjunto de cambios. +\item[\tplkword{file\_dels}] Lista de cadenas. Ficheros eliminados + por este conjunto de cambios. +\item[\tplkword{node}] Cadena. El hash de identificación de este + conjunto de cambios como una cadena hexadecimal de 40 caracteres. +\item[\tplkword{parents}] Lista de cadenas. Los ancestros del + conjunto de cambios. +\item[\tplkword{rev}] Entero. El número de revisión del repositorio + local. +\item[\tplkword{tags}] Lista de cadenas. Todas las etiquetas + asociadas al conjunto de cambios. +\end{itemize} + +Unos experimentos sencillos nos mostrarán qué esperar cuando usamos +estas palabras claves; puede ver los resultados en la +figura~\ref{fig:template:keywords}. + +\begin{figure} + \interaction{template.simple.keywords} + \caption{Template keywords in use} + \label{fig:template:keywords} +\end{figure} + +Como mencionamos anteriormente, la palabra clave de fecha no produce +salida legible por un humano, debemos tratarla de forma especial. +Esto involucra usar un \emph{filtro}, acerca de lo cual hay más en la +sección~\ref{sec:template:filter}. + +\interaction{template.simple.datekeyword} + +\section{Secuencias de Control} +\label{sec:template:escape} + +El motor de plantillas de Mercurial reconoce las secuencias de control +más comunmente usadas dentro de las cadenas. Cuando ve un backslash +(``\Verb+\+''), ve el caracter siguiente y sustituye los dos +caracteres con un reemplazo sencillo, como se describe a continuación: + +\begin{itemize} +\item[\Verb+\textbackslash\textbackslash+] Backslash, ``\Verb+\+'', + ASCII~134. +\item[\Verb+\textbackslash n+] Nueva línea, ASCII~12. +\item[\Verb+\textbackslash r+] Cambio de línea, ASCII~15. +\item[\Verb+\textbackslash t+] Tab, ASCII~11. +\item[\Verb+\textbackslash v+] Tab Vertical, ASCII~13. +\item[\Verb+\textbackslash \{+] Corchete abierto, ``\Verb+{+'', ASCII~173. +\item[\Verb+\textbackslash \}+] Corchete cerrado, ``\Verb+}+'', ASCII~175. +\end{itemize} + +Como se indicó arriba, si desea que la expansión en una plantilla +contenga un caracter literal ``\Verb+\+'', ``\Verb+{+'', o + ``\Verb+{+'', debe escaparlo. + +\section{Uso de filtros con palabras claves} +\label{sec:template:filter} + +Algunos de los resultados de la expansión de la plantilla no son +fáciles de usar de inmediato. Mercurial le permite especificar una +cadena de \emph{filtros} opcionales para modificar el resultado de +expandir una palabra clave. Ya ha visto el filtro usual +\tplkwfilt{date}{isodate} en acción con anterioridad para hacer +legible la fecha. + +A continuación hay una lista de los filtros de Mercurial más +comunmente usados. Ciertos filtros pueden aplicarse a cualquier +texto, otros pueden usarse únicamente en circunstancias específicas. +El nombre de cada filtro está seguido de la indicación de dónde puede +ser usado y una descripción de su efecto. + +\begin{itemize} +\item[\tplfilter{addbreaks}] Cualquier texto. Añade una etiqueta XHTML + ``\Verb+
    +'' antes del final de cada línea excepto en la final. + Por ejemplo, ``\Verb+foo\nbar+'' se convierte en ``\Verb+foo
    \nbar+''. +\item[\tplkwfilt{date}{age}] palabra clave \tplkword{date}. Muestra + la edad de la fecha, relativa al tiempo actual. Ofrece una cadena como + ``\Verb+10 minutes+''. +\item[\tplfilter{basename}] Cualquier texto, pero de utilidad sobre + todo en palabras claves relativas a \tplkword{ficheros}. Trata el + texto como una ruta, retornando el nombre base. Por ejemplo, + ``\Verb+foo/bar/baz+'', se convierte en ``\Verb+baz+''. +\item[\tplkwfilt{date}{date}] \tplkword{date} palabra clave. Mostrar + la fecha en un formato similar a la orden \tplkword{date} de + in a similar format to the Unix, pero con la zona horaria incluída. + Una cadena como ``\Verb+Mon Sep 04 15:13:13 2006 -0700+''. +\item[\tplkwfilt{author}{domain}] Cualquier texto, pero de mayor + utilidad para la palabra clave \tplkword{author}. Encuentra la + primera cadena que luce como una dirección de correo electrónico, y + extrae solamente el componente del dominio. Por ejemplo, de + ``\Verb+Bryan O'Sullivan +'' se extrae + ``\Verb+serpentine.com+''. +\item[\tplkwfilt{author}{email}] Cualquier texto, pero de mayor + utilidad para la palabra clave \tplkword{author}. Extrae la primera + cadena que luce como una dirección de correo. Por ejemplo, de + ``\Verb+Bryan O'Sullivan +'' extrae + ``\Verb+bos@serpentine.com+''. +\item[\tplfilter{escape}] Cualquier texto. Reemplaza los caracteres + especiales de XML/XHTML: ``\Verb+&+'', ``\Verb+<+'' y ``\Verb+>+'' + con las entidades XML. +\item[\tplfilter{fill68}] Cualquier texto. Lograr que el texto ocupe + las primeras 68 columnas. Es útil emplearlo antes de pasar el texto + por el filtro \tplfilter{tabindent}, y queremos que aún quepa en una + ventana de fuente fija y 80 columnas. +\item[\tplfilter{fill76}] Cualquier texto. Lograr que el texto quepa + en 76 columnas. +\item[\tplfilter{firstline}] Cualquier texto. Mostrar la primera + línea de texto sin saltos de línea. +\item[\tplkwfilt{date}{hgdate}] \tplkword{date} palabra clave. + Mostrar la fecha como un par de números legibles. Muestra una + cadena como ``\Verb+1157407993 25200+''. +\item[\tplkwfilt{date}{isodate}] \tplkword{date} palabra clave. + Mostrar la fecha como una cadena de texto en el formato. Muestra + una cadena como ``\Verb+2006-09-04 15:13:13 -0700+''. +\item[\tplfilter{obfuscate}] Cualquier texto, pero de mayor utilidad + para la palabra clave \tplkword{author}. Muestra el campo de texto + como una secuencia de entidades XML. Esto ayuda a eliminar ciertos + robots estúpidos de adquisición de correo. +\item[\tplkwfilt{author}{person}] Cualquier texto, útil sobre todo + para la palabra clave \tplkword{author}. Muestra el texto que hay + antes de la dirección de correo electrónico. Por ejemplo, + ``\Verb+Bryan O'Sullivan +'' mostraría + ``\Verb+Bryan O'Sullivan+''. +\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} palabra clave. + Muestra una fecha con el mismo formato que se usa en los encabezados + de correo. Mostraría una cadena como + ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+''. +\item[\tplkwfilt{node}{short}] Hash del conjunto de cambios. Muestra + la forma corta de un hash de conjunto de cambios, + of a changeset hash, p.e.~una cadena hexadecimal de 12 bytes. +\item[\tplkwfilt{date}{shortdate}] \tplkword{date} palabra clave. + Mostrar año, mes y día de una fecha. Muestrauna cadena como + ``\Verb+2006-09-04+''. +\item[\tplfilter{strip}] Cualquier texto. Elimina todos los espacios + en blanco al principio y al final de la cadena. +\item[\tplfilter{tabindent}] Cualquier texto. Muestra el texto con + todas las líneas excepto la primera que comience con el caracter tab. +\item[\tplfilter{urlescape}] Cualquier texto. Escapa todos los + caracteres que se consideren como ``especiales'' por los parsers de + URL. Por ejemplo, \Verb+foo bar+ se convierte en \Verb+foo%20bar+. +\item[\tplkwfilt{author}{user}] Cualquier texto, útil sobre todo para + la palabra clave \tplkword{author}. Retorna el ``usuario'' de una + dirección de correo. Por ejemplo, + ``\Verb+Bryan O'Sullivan +'' se convierte en + ``\Verb+bos+''. +\end{itemize} + +\begin{figure} + \interaction{template.simple.manyfilters} + \caption{Filtros de plantilla en acción} + \label{fig:template:filters} +\end{figure} + +\begin{note} + Si trata de aplicar un filtro a una porción de datos que no puede + procesarse, Mercurial fallará e imprimirá una excepción de Python. + Por ejemplo, el tratar de usar la salida de la palabra clave + \tplkword{desc} con el filtro \tplkwfilt{date}{isodate} no resultará + algo útil. +\end{note} + +\subsection{Combinar filtros} + +Combinar filtros es para generar una salida en la forma como usted lo +desea es muy sencillo. La cadena de filtros siguientes arman una +descripción, después aseguran que cabe limpiamente en 68 columnas, y +las indenta con 8~caracteres (por lo menos en sistemas tipo Unix, en +los que el tab por convención se extiende en 8~caracteres). + +\interaction{template.simple.combine} + +Observe el uso de ``\Verb+\t+'' (un caracter tab) en la plantilla para +forzar que la primera línea se indente; esto es necesario para lograr +que la primera línea luzca indentada; es necesario debido a que +\tplkword{tabindent} indenta todas las líneas \emph{excepto} la primera. + +Tenga en cuenta que el orden de los filtros importa. El primer filtro +se aplica primero al resultado de la palabra clave; el segundo al +resultado de la aplicación del primer filtro y así sucesivamente. Por +ejemplo, usar \Verb+fill68|tabindent+ es muy distinto al resultado de +usar \Verb+tabindent|fill68+. + + +\section{De plantillas a estilos} + +Una plantilla provee una forma rápida y sencilla para dar formato a +una salida. Las plantillas pueden volvers verbosas, y es útil poder +darle un nombre a una plantilla. Un fichero de estilo es una +plantilla con un nombre, almacenado en un fichero. + +Más aún, al usar un fichero de estilo se dispara el poder del motor de +plantillas en un nivel imposible de alcanzar usando las opción +\hgopt{log}{--template} desde la línea de órdenes. + + +\subsection{Los ficheros de estilo más sencillos} + +Nuestro fichero sencillo de estilo contiene una sola línea: + +\interaction{template.simple.rev} + +Se le indica a Mercurial, ``si está imprimiendo un conjunto de +cambios, use el texto de la derecha como la plantilla''. + +\subsection{Sintaxis de ficheros de estilo} + +Las reglas de sintaxis para un fichero de estilo son sencillas: + +\begin{itemize} +\item El fichero se procesa línea por línea. + +\item Se ignoran el espacio en blanco circundante. + +\item Se omiten las líneas en blanco. + +\item Si una línea comienza con los caracteres ``\texttt{\#}'' o + ``\texttt{;}'', la línea completa se trata como un comentario, y se + omite como si fuera vacía. + +\item Una línea comienza con una palabra clave. Esta debe comenzar + con una caracter alfabético o una raya al piso, y puede contener + subsecuentemente cualquier caracter alfanumérico o una raya al + piso. (En notación de expresiones regulares debe coincidir con + \Verb+[A-Za-z_][A-Za-z0-9_]*+.) + +\item El próximo elemento debe ser un caracter ``\texttt{=}'', que + puede estar precedido o seguido por una cantidad arbitraria de + espacio. + +\item Si el resto de la línea comienza y termina con caracteres + encerrados entre caracteres de comillas (bien sea sencillas o + dobles), se trata como cuerpo de la plantilla. + +\item Si el resto de la línea \emph{no} comienza con una comilla, se + trata como el nombre de un fichero; los contenidos de este fichero + se leerán y se usarán como cuerpo de la plantilla. +\end{itemize} + +\section{Ejemplos de ficheros de estilos} + +Para ilustrar la creación de un fichero de estilo, construiremos +algunos ejemplos. En lugar de ofrecer un fichero completo de estilo y +analizarlo, replicaremos el proceso usual de desarrollo de un fichero +de estilo comenzando con algo muy sencillo, y avanzando por una serie +de ejemplos sucesivos más completos. + +\subsection{Identificar equivocaciones en ficheros de estilo} + +Si Mercurial encuentra un problema en un fichero de estilo en el cual +usted está trabajando, imprime un mensaje de error suscinto, cuando +usted identifique lo que significa, resulta muy útil. + +\interaction{template.svnstyle.syntax.input} + +Tenga en cuenta que \filename{broken.style} trata de definir la +palabra clave \texttt{changeset}, pero omite dar un contenido para esta. +Cuando se le indica a Mercurial que use este fichero de estilo, se +queja inmediatamente. + +\interaction{template.svnstyle.syntax.error} + +Este mensaje de error luce intimidante, pero no es muy difícil de +seguir: + +\begin{itemize} +\item El primer componente es la forma como Mercurial dice ``me rindo''. + \begin{codesample4} + \textbf{abort:} broken.style:1: parse error + \end{codesample4} + +\item A continuación viene el nombre del fichero que contiene el error. + \begin{codesample4} + abort: \textbf{broken.style}:1: parse error + \end{codesample4} + +\item Siguendo el nombre del fichero viene el número de línea en la + que se encontró el error. + \begin{codesample4} + abort: broken.style:\textbf{1}: parse error + \end{codesample4} + +\item Finalmente, la descripción de lo que falló. + \begin{codesample4} + abort: broken.style:1: \textbf{parse error} + \end{codesample4} + La descripción del problema no siempre es clara (como en este caso), + pero aunque sea críptica, casi siempre es trivial la inspección + visual de la línea en el fichero de estilo y encontrar lo que está + mal. +\end{itemize} + +\subsection{Identificar de forma única un repositorio} + +Si desea identificar un repositorio de Mercurial ``de forma única'' +con una cadena corta como identificador, puede usar la primera +revisión en el repositorio. +\interaction{template.svnstyle.id} +No es garantía de unicidad, pero no es útill en ciertos casos: +many cases. +\begin{itemize} +\item No funcionará en un repositorio completamente vacío, porque un + repositorio así no tiene una revisión~zero. +\item Tampoco funcionará en caso (muy raro) cuando el repositorio sea + una fusión de dos repositorios independientes y tiene los dos + directorios por ahí. +\end{itemize} +Hay ciertos casos en los cuales podría colocar el identificador: +\begin{itemize} +\item Como una llave en la tabla de una base de datos que administra + repositorios en un servidor. +\item Como una parte del par \{\emph{ID~repositorio}, \emph{ID~revisión}\}. + Almacene esta información de forma independiente cuando ejecute + construcciones automatizadas u otras actividades, de forma que pueda + ``reconstruir'' posteriormente en caso de ser necesario. +\end{itemize} + +\subsection{Mostrando salida parecida a Subversion} + +Intentemos emular la salida usual que usa otro sistema de control de +revisiones, Subversion. +\interaction{template.svnstyle.short} + +Dado que la salida de Subversion es sencilla, es fácil copiar y pegar +una porción de su salida en un fichero, y reemplazar el texto +producido previamente por Subversion con valores base que quisiéramos +ver expandidos. +\interaction{template.svnstyle.template} + +Esta plantilla difiere en algunos detalles de la salida producida por +Subversion: +\begin{itemize} +\item Subversion imprime una fecha ``legible'' (el ``\texttt{Wed, 27 Sep + 2006}'' en el ejemplo de salida anterior) en paréntesis. El motor + de plantillas de Mercurial no ofrece una forma sencilla de desplegar + una fecha en este formato sin imprimir también la hora y la zona horaria. +\item Emulamos las líneas de ``separación'' de subversion con caracteres + ``\texttt{-}'' en una línea. Usamos la palabra clave + \tplkword{header} del motor de plantillas para imprimir una línea de + separación como la primera línea de salida (ver más abajo), para + lograr una salida similara a la de Subversion. +\item La salida de subversion incluye un conteo en el encabezado del + número de líneas en el mensaje de consinación. No podemos + replicarlo en Mercurial; el motor de plantilla no ofrece en la + actualidad un filtro que cuente la cantidad de objetos que se le + pasen. +\end{itemize} +No me tomó más de un minuto o dos de trabajo para reemplazar texto +literal de un ejemplo de salida de la salida de Subversion con ciertas +palabras claves y filtros para ofrecer la plantilla anterior. El +fichero de estilo se refiere sencillamente a la plantilla. +\interaction{template.svnstyle.style} + +Podríamos haber incluído el texto del fichero plantilla directamente +en el fichero de estilo encerrando entre comillas y reemplazando las +nuevas líneas con secuencias ``\verb!\n!'', pero haría muy difícil de +leer el fichero de estilos. La facilidad para leer es importante +cuando está decidiendo si un texto pertenece a un fichero de estilo o +a un fichero de plantilla incluído en el estilo. Si el fichero de +estilo luce muy grande o complicado, si inserta una pieza de texto +literal, mejor colóquelo en una plantilla. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-basic.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-basic.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,690 @@ +\chapter{Una gira de Mercurial: lo básico} +\label{chap:tour-basic} + +\section{Instalar Mercurial en su sistema} +\label{sec:tour:install} +Hay paquetes binarios precompilados de Mercurial disponibles para cada +sistema operativo popular. Esto hace fácil empezar a usar Mercurial +en su computador inmediatamente. + +\subsection{Linux} + +Dado que cada distribución de Linux tiene sus propias herramientas de +manejo de paquetes, políticas, y ritmos de desarrollo, es difícil dar +un conjunto exhaustivo de instrucciones sobre cómo instalar el paquete +de Mercurial. La versión de Mercurial que usted tenga a disposición +puede variar dependiendo de qué tan activa sea la persona que mantiene +el paquete para su distribución. + +Para mantener las cosas simples, me enfocaré en instalar Mercurial +desde la línea de comandos en las distribuciones de Linux más +populares. La mayoría de estas distribuciones proveen administradores +de paquetes gráficos que le permitirán instalar Mercurial con un solo +clic; el nombre de paquete a buscar es \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] El paquete de Mercurial de Ubuntu está basado en el de + Debian. Para instalarlo, ejecute el siguiente comando. + \begin{codesample4} + apt-get install mercurial + \end{codesample4} + El paquete de Mercurial para Ubuntu tiende a atrasarse con respecto + a la versión de Debian por un margen de tiempo considerable + (al momento de escribir esto, 7 meses), lo que en algunos casos + significará que usted puede encontrarse con problemas que ya habrán + sido resueltos en el paquete de Debian. +\end{itemize} + +\subsection{Solaris} + +SunFreeWare, en \url{http://www.sunfreeware.com}, es una buena fuente +para un gran número de paquetes compilados para Solaris para las +arquitecturas Intel y Sparc de 32 y 64 bits, incluyendo versiones +actuales de Mercurial. + +\subsection{Mac OS X} + +Lee Cantey publica un instalador de Mercurial para Mac OS~X en +\url{http://mercurial.berkwood.com}. Este paquete funciona en tanto +en Macs basados en Intel como basados en PowerPC. Antes de que pueda +usarlo, usted debe instalar una versión compatible de Universal +MacPython~\cite{web:macpython}. Esto es fácil de hacer; simplemente +siga las instrucciones del sitio de Lee. + +También es posible instalar Mercurial usando Fink o MacPorts, dos +administradores de paquetes gratuitos y populares para Mac OS X. Si +usted tiene Fink, use \command{sudo apt-get install mercurial-py25}. +Si usa MacPorts, \command{sudo port install mercurial}. + +\subsection{Windows} + +Lee Cantey publica un instalador de Mercurial para Windows en +\url{http://mercurial.berkwood.com}. Este paquete no tiene +% TODO traducción de it just works. Agreed? +dependencias externas; ``simplemente funciona''. + +\begin{note} + La versión de Windows de Mercurial no convierte automáticamente + los fines de línea entre estilos Windows y Unix. Si usted desea + compartir trabajo con usuarios de Unix, deberá hacer un trabajo + adicional de configuración. XXX Terminar esto. +\end{note} + +\section{Arrancando} + +Para empezar, usaremos el comando \hgcmd{version} para revisar si +Mercurial está instalado adecuadamente. La información de la versión +que es impresa no es tan importante; lo que nos importa es si imprime +algo en absoluto. + +\interaction{tour.version} + +% TODO builtin-> integrado? +\subsection{Ayuda integrada} + +Mercurial provee un sistema de ayuda integrada. Esto es invaluable +para ésas ocasiones en la que usted está atorado tratando de recordar +cómo ejecutar un comando. Si está completamente atorado, simplemente +ejecute \hgcmd{help}; esto imprimirá una breve lista de comandos, +junto con una descripción de qué hace cada uno. Si usted solicita +ayuda sobre un comando específico (como abajo), se imprime información +más detallada. +\interaction{tour.help} +Para un nivel más impresionante de detalle (que usted no va a +necesitar usualmente) ejecute \hgcmdargs{help}{\hggopt{-v}}. La opción +\hggopt{-v} es la abreviación para \hggopt{--verbose}, y le indica a +Mercurial que imprima más información de lo que haría usualmente. + +\section{Trabajar con un repositorio} + +En Mercurial, todo sucede dentro de un \emph{repositorio}. El +repositorio para un proyecto contiene todos los ficheros que +``pertenecen a'' ése proyecto, junto con un registro histórico de los +ficheros de ese proyecto. + +No hay nada particularmente mágico acerca de un repositorio; es +simplemente un árbol de directorios en su sistema de ficheros que +Mercurial trata como especial. Usted puede renombrar o borrar un +repositorio en el momento que lo desee, usando bien sea la línea de +comandos o su explorador de ficheros. + +\subsection{Hacer una copia local de un repositorio} + +\emph{Copiar} un repositorio es sólo ligeramente especial. Aunque +usted podría usar un programa normal de copia de ficheros para hacer +una copia del repositorio, es mejor usar el comando integrado que +Mercurial ofrece. Este comando se llama \hgcmd{clone}\ndt{Del término +``clonar'' en inglés.}, porque crea una copia idéntica de un +repositorio existente. +\interaction{tour.clone} +Si nuestro clonado tiene éxito, deberíamos tener un directorio local +llamado \dirname{hello}. Este directorio contendrá algunos ficheros. +\interaction{tour.ls} +Estos ficheros tienen el mismo contenido e historial en nuestro +repositorio y en el repositorio que clonamos. + +Cada repositorio Mercurial está completo, es autocontenido e +independiente. Contiene su propia copia de los ficheros y el historial +de un proyecto. Un repositorio clonado recuerda la ubicación de la que +fue clonado, pero no se comunica con ese repositorio, ni con ningún +otro, a menos que usted le indique que lo haga. + +Lo que esto significa por ahora es que somos libres de experimentar +con nuestro repositorio, con la tranquilidad de saber que es una +% TODO figure out what to say instead of sandbox +``caja de arena'' privada que no afectará a nadie más. + +\subsection{Qué hay en un repositorio?} + +Cuando miramos en detalle dentro de un repositorio, podemos ver que +contiene un directorio llamado \dirname{.hg}. Aquí es donde Mercurial +mantiene todos los metadatos del repositorio. +\interaction{tour.ls-a} + +Los contenidos del directorio \dirname{.hg} y sus subdirectorios son +exclusivos de Mercurial. Usted es libre de hacer lo que desee con +cualquier otro fichero o directorio en el repositorio. + +Para introducir algo de terminología, el directorio \dirname{.hg} es +el repositorio ``real'', y todos los ficheros y directorios que +coexisten con él están en el \emph{directorio de trabajo}. Una forma +sencilla de recordar esta distinción es que el \emph{repositorio} +contiene el \emph{historial} de su proyecto, mientras que el +\emph{directorio de trabajo} contiene una \emph{instantánea} de su +proyecto en un punto particular del historial. + +\section{Vistazo rápido al historial} + +Una de las primeras cosas que se desea hacer con un repositorio nuevo, +poco conocido, es conocer su historial. El comando \hgcmd{log} nos +permite ver el mismo. +\interaction{tour.log} +Por defecto este programa imprime un párrafo breve por cada cambio al +proyecto que haya sido grabado. Dentro de la terminología de +Mercurial, cada uno de estos eventos es llamado \emph{conjunto de +cambios}, porque pueden contener un registro de cambios a varios +ficheros. + +Los campos de la salida de \hgcmd{log} son los siguientes. +\begin{itemize} + \item[\texttt{changeset}]\hspace{-0.5em}\ndt{Conjunto de cambios.} Este campo + tiene un número, seguido por un + % TODO digo mejor seguido por un dos puntos ? string => + % cadena? + \texttt{:}, seguido por una cadena hexadecimal. Ambos son + \emph{identificadores} para el conjunto de cambios. Hay dos + identificadores porque el número es más corto y más fácil de + recordar que la cadena hexadecimal. + +\item[\texttt{user}]\hspace{-0.5em}\ndt{Usuario.} La identidad de la + persona que creó el conjunto de cambios. Este es un campo en el + que se puede almacenar cualquier valor, pero en la mayoría de los + casos contiene el nombre de una persona y su dirección de correo + electrónico. + +\item[\texttt{date}]\hspace{-0.5em}\ndt{Fecha.} La fecha y hora en la + que el conjunto de cambios fue creado, y la zona horaria en la que + fue creado. (La fecha y hora son locales a dicha zona horaria; + ambos muestran la fecha y hora para la persona que creó el + conjunto de cambios). + +\item[\texttt{summary}]\hspace{-0.5em}\ndt{Sumario.} + La primera línea del texto que usó la persona que creó el conjunto + de cambios para describir el mismo. +\end{itemize} +El texto impreso por \hgcmd{log} es sólo un sumario; omite una gran +cantidad de detalles. + +La figura~\ref{fig:tour-basic:history} es una representación +gráfica del historial del repositorio \dirname{hello}, para hacer más +fácil ver en qué dirección está ``fluyendo'' el historial. Volveremos +a esto varias veces en este capítulo y en los siguientes. + +\begin{figure}[ht] + \centering + \grafix{tour-history} + \caption{Historial gráfico del repositorio \dirname{hello}} + \label{fig:tour-basic:history} +\end{figure} + +\subsection{Conjuntos de cambios, revisiones, y comunicándose con +otras personas} + +%TODO sloppy => desordenado ? TODO hablar del inglés? o de español? +Ya que el inglés es un lenguaje notablemente desordenado, y el área de +ciencias de la computación tiene una notable historia de confusión de +% TODO insertar ? al revés. no sé cómo en un teclado de estos. +términos (porqué usar sólo un término cuando cuatro pueden servir?), +el control de revisiones tiene una variedad de frases y palabras que +tienen el mismo significado. Si usted habla acerca del historial de +Mercurial con alguien, encontrará que la expresión ``conjunto de +cambios'' es abreviada a menudo como ``cambio'' o (por escrito) +``cset''\ndt{Abreviatura para la expresión ``changeset'' en inglés.}, +y algunas veces un se hace referencia a un conjunto de cambios como +una ``revisión'' o ``rev''\ndt{De nuevo, como abreviación para el +término en inglés para ``revisión'' (``revision'').}. + +Si bien no es relevante qué \emph{palabra} use usted para referirse al +concepto ``conjunto de cambios'', el \emph{identificador} que usted +use para referise a ``un \emph{conjunto de cambios} particular'' es +muy importante. Recuerde que el campo \texttt{changeset} en la salida +de \hgcmd{log} identifica un conjunto de cambios usando tanto un +número como una cadena hexadecimal. + +\begin{itemize} + \item El número de revisión \emph{sólo es válido dentro del + repositorio}. + \item Por otro lado, la cadena hexadecimal es el + \emph{identificador permanente e inmutable} que siempre + identificará ése conjunto de cambios en \emph{todas} las + copias del repositorio. +\end{itemize} +La diferencia es importante. Si usted le envía a alguien un correo +electrónico hablando acerca de la ``revisión~33'', hay una +probabilidad alta de que la revisión~33 de esa persona \emph{no sea la +misma suya}. Esto sucede porque el número de revisión depende del +orden en que llegan los cambios al repositorio, y no hay ninguna +garantía de que los mismos cambios llegarán en el mismo orden en +diferentes repositorios. Tres cambios dados $a,b,c$ pueden aparecer en +un repositorio como $0,1,2$, mientras que en otro aparecen como +$1,0,2$. + +Mercurial usa los números de revisión simplemente como una abreviación +conveniente. Si usted necesita hablar con alguien acerca de un +conjunto de cambios, o llevar el registro de un conjunto de cambios +por alguna otra razón (por ejemplo, en un reporte de fallo), use el +identificador hexadecimal. + +\subsection{Ver revisiones específicas} + +Para reducir la salida de \hgcmd{log} a una sola revisión, use la +opción \hgopt{log}{-r} (o \hgopt{log}{--rev}). Puede usar un número +de revisión o un identificador hexadecimal de conjunto de cambios, y +puede pasar tantas revisiones como desee. + +\interaction{tour.log-r} + +Si desea ver el historial de varias revisiones sin tener que mencionar +cada una de ellas, puede usar la \emph{notación de rango}; esto le +permite expresar el concepto ``quiero ver todas las revisiones entre +$a$ y $b$, inclusive''. +\interaction{tour.log.range} +Mercurial también respeta el orden en que usted especifica las +revisiones, así que \hgcmdargs{log}{-r 2:4} muestra $2,3,4$ mientras +que \hgcmdargs{log}{-r 4:2} muestra $4,3,2$. + +\subsection{Información más detallada} +Aunque la información presentada por \hgcmd{log} es útil si usted sabe +de antemano qué está buscando, puede que necesite ver una descripción +completa del cambio, o una lista de los ficheros que cambiaron, si +está tratando de averiguar si un conjunto de cambios dado es el que +usted está buscando. La opción \hggopt{-v} (or \hggopt{--verbose}) del +comando \hgcmd{log} le da este nivel extra de detalle. +\interaction{tour.log-v} + +Si desea ver tanto la descripción como el contenido de un cambio, +añada la opción \hgopt{log}{-p} (o \hgopt{log}{--patch}). Esto muestra +% TODO qué hacemos con diff unificado? convervarlo, por ser la +% acepción usual? +el contenido de un cambio como un \emph{diff unificado} (si usted +nunca ha visto un diff unificado antes, vea la +sección~\ref{sec:mq:patch} para un vistazo global). +\interaction{tour.log-vp} + +\section{Todo acerca de las opciones para comandos} + +Tomemos un breve descanso de la tarea de explorar los comandos de +Mercurial para hablar de un patrón en la manera en que trabajan; será +útil tener esto en mente a medida que avanza nuestra gira. + +Mercurial tiene un enfoque directo y consistente en el manejo de las +opciones que usted le puede pasar a los comandos. Se siguen las +convenciones para opciones que son comunes en sistemas Linux y Unix +modernos. +\begin{itemize} +\item Cada opción tiene un nombre largo. Por ejemplo, el comando + \hgcmd{log} acepta la opción \hgopt{log}{--rev}, como ya hemos + visto. +\item Muchas opciones tienen también un nombre corto. En vez de + \hgopt{log}{--rev}, podemos usar \hgopt{log}{-r}. (El motivo para + que algunas opciones no tengan nombres cortos es que dichas + opciones se usan rara vez.) +\item Las opciones largas empiezan con dos guiones (p.ej.~\hgopt{log}{--rev}), + mientras que las opciones cortas empiezan con uno (e.g.~\hgopt{log}{-r}). +\item El nombre y uso de las opciones es consistente en todos los + comandos. Por ejemplo, cada comando que le permite pasar un ID de + conjunto de cambios o un número de revisión acepta tanto la opción + \hgopt{log}{-r} como la \hgopt{log}{--rev}. +\end{itemize} +En los ejemplos en este libro, uso las opciones cortas en vez de las +largas. Esto sólo muestra mis preferencias, así que no le dé +significado especial a eso. + +Muchos de los comandos que generan salida de algún tipo mostrarán más +salida cuando se les pase la opción \hggopt{-v} (o +\hggopt{--verbose}\ndt{Prolijo.}), y menos cuando se les pase la opción \hggopt{-q} +(o \hggopt{--quiet}\ndt{Silencioso.}). + +\section{Hacer y repasar cambios} + +Ahora que tenemos una comprensión adecuada sobre cómo revisar el +historial en Mercurial, hagamos algunos cambios y veamos cómo +examinarlos. + +Lo primero que haremos será aislar nuestro experimento en un +repositorio propio. Usaremos el comando \hgcmd{clone}, pero no hace +falta clonar una copia del repositorio remoto. Como ya contamos con +una copia local del mismo, podemos clonar esa. Esto es mucho más +rápido que clonar a través de la red, y en la mayoría de los casos +clonar un repositorio local usa menos espacio en disco también. +\interaction{tour.reclone} +A manera de recomendación, es considerado buena práctica mantener una +copia ``prístina'' de un repositorio remoto a mano, del cual usted +puede hacer clones temporales para crear cajas de arena para cada +tarea en la que desee trabajar. Esto le permite trabajar en múltiples +tareas en paralelo, teniendo cada una de ellas aislada de las otras +hasta que estén completas y usted esté listo para integrar los cambios +de vuelta. Como los clones locales son tan baratos, clonar y destruir +repositorios no consume demasiados recursos, lo que facilita hacerlo +en cualquier momento. + +En nuestro repositorio \dirname{my-hello}, hay un fichero +\filename{hello.c} que contiene el clásico programa ``hello, +world''\ndt{Hola, mundo.}. Usaremos el clásico y venerado comando +\command{sed} para editar este fichero y hacer que imprima una segunda +línea de salida. (Estoy usando el comando \command{sed} para hacer +esto sólo porque es fácil escribir un ejemplo automatizado con él. +Dado que usted no tiene esta restricción, probablemente no querrá usar +\command{sed}; use su editor de texto preferido para hacer lo mismo). +\interaction{tour.sed} + +El comando \hgcmd{status} de Mercurial nos dice lo que Mercurial sabe +acerca de los ficheros en el repositorio. +\interaction{tour.status} +El comando \hgcmd{status} no imprime nada para algunos ficheros, sólo +una línea empezando con ``\texttt{M}'' para el fichero +\filename{hello.c}. A menos que usted lo indique explícitamente, +\hgcmd{status} no imprimirá nada respecto a los ficheros que no han +sido modificados. + +La ``\texttt{M}'' indica que Mercurial se dio cuenta de que nosotros +modificamos \filename{hello.c}. No tuvimos que \emph{decirle} a +Mercurial que íbamos a modificar ese fichero antes de hacerlo, o que +lo modificamos una vez terminamos de hacerlo; él fue capaz de darse +cuenta de esto por sí mismo. + +Es algo útil saber que hemos modificado el fichero \filename{hello.c}, +pero preferiríamos saber exactamente \emph{qué} cambios hicimos. +Para averiguar esto, usamos el comando \hgcmd{diff}. +\interaction{tour.diff} + +\section{Grabar cambios en un nuevo conjunto de cambios} + +Podemos modificar, compilar y probar nuestros cambios, y usar +\hgcmd{status} y \hgcmd{diff} para revisar los mismos, hasta que +estemos satisfechos con los resultados y lleguemos a un momento en el +que sea natural que querramos guardar nuestro trabajo en un nuevo +conjunto de cambios. + +El comando \hgcmd{commit} nos permite crear un nuevo conjunto de +cambios. Nos referiremos usualmente a esto como ``hacer una consigna'' +o consignar. + +\subsection{Definir un nombre de usuario} + +Cuando usted trata de ejecutar \hgcmd{commit}\ndt{Hacer una +consignación} por primera vez, no está garantizado que lo logre. +Mercurial registra su nombre y dirección en cada cambio que usted +consigna, para que más adelante otros puedan saber quién es el +responsable de cada cambio. Mercurial trata de encontrar un nombre de +% TODO consigna o consignación? +usuario adecuado con el cual registrar la consignación. Se intenta con +cada uno de los siguientes métodos, en el orden presentado. +\begin{enumerate} +\item Si usted pasa la opción \hgopt{commit}{-u} al comando \hgcmd{commit} + en la línea de comandos, seguido de un nombre de usuario, se le da a + esto la máxima precedencia. +\item A continuación se revisa si usted ha definido la variable de + entorno \envar{HGUSER}. +\item Si usted crea un fichero en su directorio personal llamado + \sfilename{.hgrc}, con una entrada \rcitem{ui}{username}, se usa + luego. Para revisar cómo debe verse este fichero, refiérase a la + sección~\ref{sec:tour-basic:username} más abajo. +\item Si usted ha definido la variable de entorno \envar{EMAIL}, será + usada a continuación. +\item Mercurial le pedirá a su sistema buscar su nombre de usuario + % TODO host => máquina + local, y el nombre de máquina, y construirá un nombre de usuario a + partir de estos componentes. Ya que esto generalmente termina + generando un nombre de usuario no muy útil, se imprimirá una + advertencia si es necesario hacerlo. +\end{enumerate} +Si todos estos procedimientos fallan, Mercurial fallará, e imprimirá +un mensaje de error. En este caso, no le permitirá hacer la +consignación hasta que usted defina un nombre de usuario. + +Trate de ver la variable de entorno \envar{HGUSER} y la opción +\hgopt{commit}{-u} del comando \hgcmd{commit} como formas de +\emph{hacer caso omiso} de la selección de nombre de usuario que +Mercurial hace normalmente. Para uso normal, la manera más simple y +sencilla de definir un nombre de usuario para usted es crear un +fichero \sfilename{.hgrc}; los detalles se encuentran más adelante. + +\subsubsection{Crear el fichero de configuración de Mercurial} +\label{sec:tour-basic:username} + +Para definir un nombre de usuario, use su editor de texto favorito +para crear un fichero llamado \sfilename{.hgrc} en su directorio +personal. Mercurial usará este fichero para obtener las +configuraciones personalizadas que usted haya hecho. El contenido +inicial de su fichero \sfilename{.hgrc} debería verse así. +\begin{codesample2} + # Este es un fichero de configuración de Mercurial. + [ui] + username = Primernombre Apellido +\end{codesample2} +La línea ``\texttt{[ui]}'' define una \emph{section} del fichero de +configuración, así que usted puede leer la línea ``\texttt{username = +...}'' como ``defina el valor del elemento \texttt{username} en la +sección \texttt{ui}''. +Una sección continua hasta que empieza otra nueva, o se llega al final +del fichero. Mercurial ignora las líneas vacías y considera cualquier +texto desde el caracter ``\texttt{\#}'' hasta el final de la línea +como un comentario. + +\subsubsection{Escoger un nombre de usuario} + +Usted puede usar el texto que desee como el valor del campo de +configuración \texttt{username}, ya que esta información será leída +por otras personas, e interpretada por Mercurial. La convención que +sigue la mayoría de la gente es usar su nombre y dirección de correo, +como en el ejemplo anterior. + +\begin{note} + % TODO web + El servidor web integrado de Mercurial ofusca las direcciones de + correo, para dificultar la tarea de las herramientas de + recolección de direcciones de correo que usan los + spammers\ndt{Personas que envían correo no solicitado, también + conocido como correo basura}. Esto reduce la probabilidad de que + usted empiece a recibir más correo basura si publica un + repositorio en la red. +\end{note} + +\subsection{Escribir un mensaje de consignación} + +Cuando consignamos un cambio, Mercurial nos ubica dentro de un editor +de texto, para ingresar un mensaje que describa las modificaciones que +hemos introducido en este conjunto de cambios. Esto es conocido como +un \emph{mensaje de consignación}. Será un registro de lo que hicimos +y porqué lo hicimos, y será impreso por \hgcmd{log} una vez hayamos +hecho la consignación. +\interaction{tour.commit} + +El editor en que \hgcmd{commit} nos ubica contendrá una línea vacía, +seguida de varias líneas que empiezan con la cadena ``\texttt{HG:}''. +\begin{codesample2} + \emph{línea vacía} + HG: changed hello.c +\end{codesample2} +Mercurial ignora las líneas que empiezan con ``\texttt{HG:}''; sólo +las usa para indicarnos para cuáles ficheros está registrando los +cambios. Modificar o borrar estas líneas no tiene ningún efecto. + +\subsection{Escribir un buen mensaje de consignación} + +Ya que por defecto \hgcmd{log} sólo muestra la primera línea de un +mensaje de consignación, lo mejor es escribir un mensaje cuya primera +línea tenga significado por sí misma. A continuación se encuentra un +ejemplo de un mensaje de consignación que \emph{no} sigue esta +pauta, y debido a ello tiene un sumario que no es legible. +\begin{codesample2} + changeset: 73:584af0e231be + user: Persona Censurada + date: Tue Sep 26 21:37:07 2006 -0700 + summary: se incluye buildmeister/commondefs. Añade un módulo +\end{codesample2} + +Con respecto al resto del contenido del mensaje de consignación, no +hay reglas estrictas-y-rápidas. Mercurial no interpreta ni le da +importancia a los contenidos del mensaje de consignación, aunque es +posible que su proyecto tenga políticas que definan una manera +particular de escribirlo. + +Mi preferencia personal es usar mensajes de consignación cortos pero +informativos, que me digan algo que no puedo inferir con una mirada +rápida a la salida de \hgcmdargs{log}{--patch}. + +\subsection{Cancelar una consignación} + +Si usted decide que no desea hacer la consignación mientras está +editando el mensaje de la misma, simplemente cierre su editor sin +guardar los cambios al fichero que está editando. Esto hará que no +pase nada ni en el repositorio ni en el directorio de trabajo. + +Si ejecutamos el comando \hgcmd{commit} sin ningún argumento, se +registran todos los cambios que hemos hecho, como lo indican +\hgcmd{status} y \hgcmd{diff}. + +\subsection{Admirar nuestro trabajo} + +Una vez hemos terminado la consignación, podemos usar el comando +\hgcmd{tip}\ndt{Punta.} para mostrar el conjunto de cambios que acabamos de crear. +La salida de este comando es idéntica a la de \hgcmd{log}, pero sólo +muestra la revisión más reciente en el repositorio. +\interaction{tour.tip} +Nos referimos a la revisión más reciente en el repositorio como la +revisión de punta, o simplemente la punta. + +\section{Compartir cambios} + +Anteriormente mencionamos que los repositorios en Mercurial están auto +contenidos. Esto quiere decir que el conjunto de cambios que acabamos +de crear sólo existe en nuestro repositorio \dirname{my-hello}. Veamos +unas cuantas formas de propagar este cambio a otros repositorios. + +\subsection{Jalar cambios desde otro repositorio} +\label{sec:tour:pull} + +Para empezar, clonemos nuestro repositorio \dirname{hello} original, +el cual no contiene el cambio que acabamos de consignar. Llamaremos a +este repositorio temporal \dirname{hello-pull}. +\interaction{tour.clone-pull} + +Usaremos el comando \hgcmd{pull} para traer los cambios de +\dirname{my-hello} y ponerlos en \dirname{hello-pull}. Sin embargo, +traer cambios desconocidos y aplicarlos en un repositorio es una +perspectiva que asusta al menos un poco. Mercurial cuenta con el +comando \hgcmd{incoming}\ndt{Entrante, o cambios entrantes.} para +decirnos qué cambios \emph{jalaría} el comando \hgcmd{pull} al +repositorio, sin jalarlos. +\interaction{tour.incoming} +(Por supuesto, alguien podría enviar más conjuntos de cambios al +repositorio en el tiempo que pasa entre la ejecución de +\hgcmd{incoming} y la ejecución de \hgcmd{pull} para jalar los +cambios, así que es posible que terminemos jalando cambios que no +esperábamos.) + +Traer cambios al repositorio simplemente es cuestión de ejecutar el +comando \hgcmd{pull}, indicándole de qué repositorio debe jalarlos. +\interaction{tour.pull} +Como puede verse por las salidas antes-y-después de \hgcmd{tip}, hemos +jalado exitosamente los cambios en nuestro repositorio. Aún falta un +paso para que podamos ver estos cambios en nuestro directorio de +trabajo. + +\subsection{Actualizar el directorio de trabajo} + +Hasta ahora hemos pasado por alto la relación entre un repositorio y +su directorio de trabajo. El comando \hgcmd{pull} que ejecutamos en la +sección~\ref{sec:tour:pull} trajo los cambios al repositorio, pero si +revisamos, no hay rastro de esos cambios en el directorio de trabajo. +Esto pasa porque \hgcmd{pull} (por defecto) no modifica el directorio de +trabajo. En vez de eso, usamos el comando +\hgcmd{update}\ndt{Actualizar.} para hacerlo. +\interaction{tour.update} + +Puede parecer algo raro que \hgcmd{pull} no actualice el directorio de +trabajo automáticamente. De hecho, hay una buena razón para esto: +usted puede usar \hgcmd{update} para actualizar el directorio de +trabajo al estado en que se encontraba en \emph{cualquier revisión} +del historial del repositorio. Si usted hubiera actualizado el +directorio de trabajo a una revisión anterior---digamos, para buscar +el origen de un fallo---y hubiera corrido un \hgcmd{pull} que hubiera +actualizado el directorio de trabajo automáticamente a la nueva +revisión, puede que no estuviera particularmente contento. + +Sin embargo, como jalar-y-actualizar es una secuencia de operaciones +muy común, Mercurial le permite combinarlas al pasar la opción +\hgopt{pull}{-u} +a \hgcmd{pull}. +\begin{codesample2} + hg pull -u +\end{codesample2} +Si mira de vuelta la salida de \hgcmd{pull} en la +sección~\ref{sec:tour:pull} cuando lo ejecutamos sin la opción \hgopt{pull}{-u}, +verá que el comando imprimió un amable recordatorio de que tenemos que +encargarnos explícitamente de actualizar el directorio de trabajo: +\begin{codesample2} + (run 'hg update' to get a working copy) +\end{codesample2} + +Para averiguar en qué revisión se encuentra el directorio de trabajo, +use el comando \hgcmd{parents}. +\interaction{tour.parents} +Si mira de nuevo la figura~\ref{fig:tour-basic:history}, verá flechas +conectando cada conjunto de cambios. En cada caso, el nodo del que la flecha +\emph{sale} es un padre, y el nodo al que la flecha \emph{llega} es +su hijo. El directorio de trabajo tiene un padre exactamente de la +misma manera; ése es el conjunto de cambios que contiene actualmente +el directorio de trabajo. + +Para actualizar el conjunto de trabajo a una revisión particular, pase +un número de revisión o un ID de conjunto de cambios al comando +\hgcmd{update}. +\interaction{tour.older} +Si no indica explícitamente una revisión, \hgcmd{update} actualizará +hasta la revisión de punta, como se vio en la segunda llamada a +\hgcmd{update} en el ejemplo anterior. + +\subsection{Empujar cambios a otro repositorio} + +Mercurial nos permite empujar cambios a otro repositorio, desde el +% TODO cambié "visitando" por "usando" +repositorio que estemos usando actualmente. De la misma forma que en +el ejemplo de \hgcmd{pull} arriba, crearemos un repositorio temporal +para empujar allí nuestros cambios. +\interaction{tour.clone-push} +El comando \hgcmd{outgoing}\ndt{Saliente. Cambios salientes.} nos dice +qué cambios serían empujados en el otro repositorio. +\interaction{tour.outgoing} +Y el comando \hgcmd{push} se encarga de empujar dichos cambios. +\interaction{tour.push} +Al igual que \hgcmd{pull}, el comando \hgcmd{push} no actualiza el +directorio de trabajo del repositorio en el que estamos empujando los +cambios. (A diferencia de \hgcmd{pull}, \hgcmd{push} no ofrece la +opción \texttt{-u} para actualizar el directorio de trabajo del otro +repositorio.) + +% TODO poner interrogante de apertura +Qué pasa si tratamos de jalar o empujar cambios y el repositorio +receptor ya tiene esos cambios? Nada emocionante. +\interaction{tour.push.nothing} + +\subsection{Compartir cambios a través de una red} + +Los comandos que hemos presentando en las pocas secciones anteriores +no están limitados a trabajar con repositorios locales. Cada uno de +ellos funciona exactamente de la misma manera a través de una conexión +% TODO poner ndt para URL +de red. Simplemente pase una URL en vez de una ruta local. +\interaction{tour.outgoing.net} +En este ejemplo, podemos ver qué cambios empujaríamos al repositorio +remoto, aunque, de manera entendible, el repositorio remoto está +configurado para no permitir a usuarios anónimos empujar cambios a él. +\interaction{tour.push.net} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-history.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + (la más nueva) + (la más antigua) + + 4: REV4 + + + número derevisión + identificador delconjunto de cambios + + diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-conflict.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Saludos!Soy Mariam Abacha, la esposa del anterior dictador de Nigeria Sani Abacha. Le contacto en secreto, buscando los medios para desarrollar + + + + + + Saludos!Soy Shehu Musa Abacha, sobrina del anterior dictador de Nigeria Sani Abacha. Le contacto en secreto, buscando los medios para desarrollar + + + + + + Saludos!Soy Alhaji Abba Abacha, hijo del anterior dictador de Nigeria Sani Abacha. Le contacto en secreto, buscando los medios para desarrollar + + + Versión inicial + Nuestros cambios + Sus cambios + + diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-merge.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + punta (y frente) + frente + + + + fusión + directorio de trabajodurante la fusión + + 4: REV4 + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + punta + + + 7: REV7_my_new_hello + Directorio de trabajo durante la fusión + Repositorio después de consignar la fusión + + diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-pull.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + + 6: REV6_my_new_hello + + tip (y principal) + principal + + diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-sep-repos.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,480 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_hello + + my-hello + + 0: REV0 + + 1: REV1 + + 2: REV2 + + 3: REV3 + + 4: REV4 + + + + + + 5: REV_my_new_hello + + my-new-hello + Los cambiosmás recientesdifieren + historia común + + + + + + + revisión principal (sin hijos) + + diff -r 5981a0f7540a -r 019040fbf5f5 es/tour-merge.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,308 @@ +\chapter{Una gira de Mercurial: fusionar trabajo} +\label{chap:tour-merge} + +Hasta ahora hemos cubierto cómo clonar un repositorio, hacer cambios, +y jalar o empujar dichos cambios de un repositorio a otro. Nuestro +siguiente paso es \emph{fusionar} cambios de repositorios separados. + +% TODO cambié streams por líneas. check please +\section{Fusionar líneas de trabajo} + +Fusionar es una parte fundamental de trabajar con una herramienta +de control distribuido de versiones. +\begin{itemize} +\item Alicia y Roberto tienen cada uno una copia personal del + repositorio de un proyecto en el que están trabajando. Alicia + arregla un fallo en su repositorio; Roberto añade una nueva + característica en el suyo. Ambos desean que el repositorio + compartido contenga el arreglo del fallo y la nueva + característica. +\item Frecuentemente trabajo en varias tareas diferentes en un mismo + proyecto al mismo tiempo, cada una aislada convenientemente de las + otras en su propio repositorio. Trabajar de esta manera significa + que a menudo debo fusionar una parte de mi propio trabajo con + otra. +\end{itemize} + +Como fusionar es una operación tan necesaria y común, Mercurial la +facilita. Revisemos el proceso. Empezaremos clonando (otro) +% TODO poner interrogante de apertura +repositorio (ve lo seguido que aparecen?) y haciendo un cambio en él. +\interaction{tour.merge.clone} +Ahora deberíamos tener dos copias de \filename{hello.c} con contenidos +diferentes. El historial de los dos repositorios diverge ahora, como +se ilustra en la figura~\ref{fig:tour-merge:sep-repos}. +\interaction{tour.merge.cat} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-sep-repos} + \caption{Historial reciente divergente de los repositorios + \dirname{my-hello} y \dirname{my-new-hello}} + \label{fig:tour-merge:sep-repos} +\end{figure} + +Ya sabemos que jalar los cambios desde nuestro repositorio +\dirname{my-hello} no tendrá efecto en el directorio de trabajo. +\interaction{tour.merge.pull} +Sin embargo, el comando \hgcmd{pull} dice algo acerca de +``frentes''\ndt{El autor se refiere a \emph{heads} aquí.}. + +\subsection{Conjuntos de cambios de frentes} + +Un frente es un cambio que no tiene descendientes, o hijos, como +también se les conoce. La revisión de punta es, por tanto, un frente, +porque la revisión más reciente en un repositorio no tiene ningún +% TODO cambio en la redacción de la frase, pero espero que conserve el +% sentido. Querido human@, apruebe o corrija :D +hijo. Sin embargo, un repositorio puede contener más de un frente. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-pull} + \caption{Contenidos del repositorio después de jalar + \dirname{my-hello} a \dirname{my-new-hello}} + \label{fig:tour-merge:pull} +\end{figure} + +En la figura~\ref{fig:tour-merge:pull} usted puede ver el efecto que +tiene jalar los cambios de \dirname{my-hello} a \dirname{my-new-hello}. +El historial que ya existía en \dirname{my-new-hello} se mantiene +intacto, pero fue añadida una nueva revisión. Refiriéndonos a la +figura~\ref{fig:tour-merge:sep-repos}, podemos ver que el \emph{ID del +conjunto de cambios} se mantiene igual en el nuevo repositorio, pero +el \emph{número de revisión} ha cambiado. (Incidentalmente, éste es un +buen ejemplo de porqué no es seguro usar números de revisión cuando se +habla de conjuntos de cambios). Podemos ver los frentes en un +repositorio usando el comando \hgcmd{heads}\ndt{Frentes.}. +\interaction{tour.merge.heads} + +\subsection{Hacer la fusión} + +% TODO poner interrogante de apertura +Qué pasa si tratamos de usar el comando usual, \hgcmd{update}, para +actualizar el nuevo frente? +\interaction{tour.merge.update} +Mercurial nos indica que el comando \hgcmd{update} no hará la fusión; +no actualizará el directorio de trabajo cuando considera que lo que +deseamos hacer es una fusión, a menos que lo obliguemos a hacerlo. +En vez de \hgcmd{update}, usamos el comando \hgcmd{merge} para hacer +la fusión entre los dos frentes. +\interaction{tour.merge.merge} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-merge} + \caption{Directorio de trabajo y repositorio durante la fusión, y + consignación consecuente} + \label{fig:tour-merge:merge} +\end{figure} + +Esto actualiza el directorio de trabajo, de tal forma que contenga los +cambios de \emph{ambos} frentes, lo que se ve reflejado tanto en la +salida de \hgcmd{parents} como en los contenidos de \filename{hello.c}. +\interaction{tour.merge.parents} + +\subsection{Consignar los resultados de la fusión} + +Siempre que hacemos una fusión, \hgcmd{parents} mostrará dos padres +hasta que consignemos (\hgcmd{commit}) los resultados de la fusión. +\interaction{tour.merge.commit} +Ahora tenemos una nueva revisión de punta; note que tiene \emph{los +dos} frentes anteriores como sus padres. Estos son las mismas +revisiones que mostró previamente el comando \hgcmd{parents}. +\interaction{tour.merge.tip} +En la figura~\ref{fig:tour-merge:merge} usted puede apreciar una +representación de lo que pasa en el directorio de trabajo durante la +fusión cuando se hace la consignación. Durante la fusión, el +directorio de trabajo tiene dos conjuntos de cambios como sus padres, +y éstos se vuelven los padres del nuevo conjunto de cambios. + +\section{Fusionar cambios con conflictos} + +La mayoría de las fusiones son algo simple, pero a veces usted se +encontrará fusionando cambios donde más de uno de ellos afecta las +mismas secciones de los mismos ficheros. A menos que ambas +modificaciones sean idénticas, el resultado es un \emph{conflicto}, en +donde usted debe decidir cómo reconciliar ambos cambios y producir un +resultado coherente. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-conflict} + \caption{Cambios con conflictos a un documento} + \label{fig:tour-merge:conflict} +\end{figure} + +La figura~\ref{fig:tour-merge:conflict} ilustra un ejemplo con dos +cambios generando conflictos en un documento. Empezamos con una sola +versión del fichero; luego hicimos algunos cambios; mientras tanto, +alguien más hizo cambios diferentes en el mismo texto. Lo que debemos +hacer para resolver el conflicto causado por ambos cambios es decidir +cómo debe quedar finalmente el fichero. + +Mercurial no tiene ninguna utilidad integrada para manejar conflictos. +En vez de eso, ejecuta un programa externo llamado \command{hgmerge}. +Es un guión de línea de comandos que es instalado junto con Mercurial; +usted puede modificarlo para que se comporte como usted lo desee. Por +defecto, lo que hace es tratar de encontrar una de varias herramientas +para fusionar que es probable que estén instaladas en su sistema. +Primero se intenta con unas herramientas para fusionar cambios +automáticamente; si esto no tiene éxito (porque la fusión demanda +una guía humana) o dichas herramientas no están presentes, el guión +intenta con herramientas gráficas para fusionar. + +También es posible hacer que Mercurial ejecute otro programa o guión +en vez de \command{hgmerge}, definiendo la variable de entorno +\envar{HGMERGE} con el nombre del programa de su preferencia. + +\subsection{Usar una herramienta gráfica para fusión} + +Mi herramienta favorita para hacer fusiones es \command{kdiff3}, y la +usaré para describir las características comunes de las herramientas +gráficas para hacer fusiones. Puede ver una captura de pantalla de +\command{kdiff3} ejecutándose, en la +figura~\ref{fig:tour-merge:kdiff3}. El tipo de fusión que la +herramienta hace se conoce como \emph{fusión de tres vías}, porque hay +tres versiones diferentes del fichero en que estamos interesados. +Debido a esto la herramienta divide la parte superior de la ventana en +tres paneles. +\begin{itemize} +\item A la izquierda está la revisión \emph{base} del fichero, p.ej.~la + versión más reciente de la que descienden las dos versiones que + estamos tratando de fusionar. +\item En la mitad está ``nuestra'' versión del fichero, con las + modificaciones que hemos hecho. +\item A la derecha está la versión del fichero de ``ellos'', la que + forma parte del conjunto de cambios que estamos tratando de + fusionar. +\end{itemize} +En el panel inferior se encuentra el \emph{resultado} actual de la +fusión. Nuestra tarea es reemplazar todo el texto rojo, que muestra +los conflictos sin resolver, con una fusión adecuada de ``nuestra'' +versión del fichero y la de ``ellos''. + +Los cuatro paneles están \emph{enlazados}; si avanzamos vertical o +horizontalmente en cualquiera de ellos, los otros son actualizados +para mostrar las secciones correspondientes del fichero que tengan +asociado. + +\begin{figure}[ht] + \centering + \grafix[width=\textwidth]{kdiff3} + \caption{Usando \command{kdiff3} para fusionar versiones de un + fichero} + \label{fig:tour-merge:kdiff3} +\end{figure} + +En cada conflicto del fichero podemos escoger resolverlo usando +cualquier combinación del texto de la revisión base, la nuestra, o la +de ellos. También podemos editar manualmente el fichero en que queda +la fusión, si es necesario hacer cambios adicionales. + +Hay \emph{muchas} herramientas para fusionar ficheros disponibles. Se +diferencian en las plataformas para las que están disponibles, y en +sus fortalezas y debilidades particulares. La mayoría están afinadas +para fusionar texto plano, mientras que otras están pensadas para +formatos de ficheros especializados (generalmente XML). + +% TODO traduje "worked" como "real" +\subsection{Un ejemplo real} + +En este ejemplo, reproduciremos el historial de modificaciones al +fichero de la figura~\ref{fig:tour-merge:conflict} mostrada +anteriormente. Empecemos creando un repositorio con la versión base +de nuestro documento. +\interaction{tour-merge-conflict.wife} +Clonaremos el repositorio y haremos un cambio al fichero. +\interaction{tour-merge-conflict.cousin} +Y haremos otro clon, para simular a alguien más haciendo un cambio al +mismo fichero. (Esto introduce la idea de que no es tan inusual hacer +fusiones consigo mismo, cuando usted aísla tareas en repositorios +separados, y de hecho encuentra conflictos al hacerlo.) +\interaction{tour-merge-conflict.son} +Ahora que tenemos dos versiones diferentes de nuestro fichero, +crearemos un entorno adecuado para hacer la fusión. +\interaction{tour-merge-conflict.pull} + +En este ejemplo, no usaré el comando normal de Mercurial para hacer la +fusión (\command{hgmerge}), porque lanzaría mi linda herramienta +automatizada para correr ejemplos dentro de una interfaz gráfica de +usuario. En vez de eso, definiré la variable de entorno +\envar{HGMERGE} para indicarle a Mercurial que use el comando +\command{merge}. Este comando forma parte de la instalación base de +muchos sistemas Unix y similares. Si usted está ejecutando este +ejemplo en su computador, no se moleste en definir \envar{HGMERGE}. +\interaction{tour-merge-conflict.merge} +Debido a que \command{merge} no puede resolver los conflictos que +aparecen, él deja \emph{marcadores de fusión} en el fichero con +conflictos, indicando si provienen de nuestra versión o de la de +ellos. + +Mercurial puede saber ---por el código de salida del comando +\command{merge}--- que no fue posible hacer la fusión exitosamente, +así que nos indica qué comandos debemos ejecutar si queremos rehacer +la fusión. Esto puede ser útil si, por ejemplo, estamos ejecutando una +herramienta gráfica de fusión y salimos de ella porque nos confundimos +o cometimos un error. + +Si la fusión ---automática o manual--- falla, no hay nada que nos +impida ``arreglar'' los ficheros afectados por nosotros mismos, y +consignar los resultados de nuestra fusión: +% TODO este mercurial no tiene el comando resolve. Revisar si sigue +% siendo necesario +\interaction{tour-merge-conflict.commit} + +\section{Simplificar el ciclo jalar-fusionar-consignar} +\label{sec:tour-merge:fetch} + +El proceso de fusionar cambios delineado anteriomente es directo, pero +requiere la ejecución de tres comandos en sucesión. +\begin{codesample2} + hg pull + hg merge + hg commit -m 'Fusionados cambios remotos' +\end{codesample2} +En la consignación final usted debe proveer un mensaje adecuado, que +casi siempre es un fragmento de texto ``de relleno'' carente de valor +particular. + +Sería agradable reducir la cantidad de pasos necesarios, si fuera +posible. De hecho, Mercurial es distribuido junto con una extensión +llamada \hgext{fetch}\ndt{Descargar, traer.} que hace precisamente +esto. + +Mercurial cuenta con un mecanismo de extensión flexible que le permite +% TODO lets people => permite a usuarios +a sus usuarios extender su funcionalidad, manteniendo el núcleo de +Mercurial pequeño y fácil de manejar. Algunas extensiones añaden +nuevos comandos que usted puede usar desde la línea de comandos, +mientras que otros funcionan ``tras bambalinas'', por ejemplo, +añadiendo funcionalidad al servidor. + +La extensión \hgext{fetch} añade un comando llamado, no +sorpresivamente, \hgcmd{fetch}. Esta extensión actúa como una +combinación de \hgcmd{pull}, \hgcmd{update} y \hgcmd{merge}. Empieza +jalando cambios de otro repositorio al repositorio actual. Si +encuentra que los cambios añaden un nuevo frente en el repositorio +actual, inicia una fusión, y luego consigna el resultado de la misma +con un mensaje generado automáticamente. Si no se añadieron nuevos +frentes, actualiza el directorio de trabajo con el nuevo conjunto de +cambios de punta. + +Activar la extensión \hgext{fetch} es fácil. Edite su +\sfilename{.hgrc}, y vaya a (o cree) la sección +\rcsection{extensions}. Luego añada una línea que diga simplemente +``\Verb+fetch +''. +\begin{codesample2} + [extensions] + fetch = +\end{codesample2} +(Normalmente, a la derecha del ``\texttt{=}'' debería aparecer la +ubicación de la extensión, pero como el comando \hgext{fetch} es parte +de la distribución estándar, Mercurial sabe dónde buscarla.) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual-merge.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +digraph undo_manual { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "tercer cambio"; + reversar [label="reversar\nsegundo cambio", shape=box]; + "segundo cambio" -> reversar; + "tercer cambio" -> "fusión\nmanual"; + reversar -> "fusión\nmanual"; +} diff -r 5981a0f7540a -r 019040fbf5f5 es/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,6 @@ +digraph undo_manual { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "tercer cambio"; + reversar [label="reversar\nsegundo cambio", shape=box]; + "segundo cambio" -> reversar; +} diff -r 5981a0f7540a -r 019040fbf5f5 es/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-non-tip.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,9 @@ +digraph undo_non_tip { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "tercer cambio"; + reversar [label="reversar\nsegundo cambio", shape=box]; + "segundo cambio" -> reversar; + merge [label="automatizar\nfusión", shape=box]; + "tercer cambio" -> fusión; + reversar -> fusión; +} diff -r 5981a0f7540a -r 019040fbf5f5 es/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-simple.dot Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,4 @@ +digraph undo_simple { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "reversar\nsegundo cambio"; +} diff -r 5981a0f7540a -r 019040fbf5f5 es/undo.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo.tex Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,799 @@ +\chapter{Encontrar y arreglar sus equivocaciones} +\label{chap:undo} + +Errar es humano, pero tratar adecuadamente las consecuencias requiere +un sistema de control de revisiones de primera categoría. En este +capítulo, discutiremos algunas técnicas que puede usar cuando +encuentra que hay un problema enraizado en su proyecto. Mercurial +tiene unas características poderosas que le ayudarán a isolar las +fuentes de los problemas, y a dar cuenta de ellas apropiadamente. + +\section{Borrar el historial local} + +\subsection{La consignación accidental} + +Tengo el problema ocasional, pero persistente de teclear más rápido de +lo que pienso, que aveces resulta en consignar un conjunto de cambios +incompleto o simplemente malo. En mi caso, el conjunto de cambios +incompleto consiste en que creé un nuevo fichero fuente, pero olvidé +hacerle \hgcmd{add}. Un conjunto de cambios``simplemente malo'' no es +tan común, pero sí resulta muy molesto. + +\subsection{Hacer rollback una transacción} +\label{sec:undo:rollback} + +En la sección~\ref{sec:concepts:txn}, mencioné que Mercurial trata +modificación a un repositorio como una \emph{transacción}. Cada vez +que consigna un conjunto de cambios o lo jala de otro repositorio, +Mercurial recuerda lo que hizo. Puede deshacer, o hacer \emph{roll back}\ndt{El significado igual que en los + ambientes de sistemas manejadores de bases de datos se refiere a + la atomicidad e integridad al devolver un conjunto de acciones que + permitan dejar el repositorio en un estado consistente previo}, +exactamente una de tales acciones usando la orden \hgcmd{rollback}. +(Ver en la sección~\ref{sec:undo:rollback-after-push} una anotación +importante acerca del uso de esta orden.) + +A continuación una equivocación que me sucede frecuentemente: +consignar un cambio en el cual he creado un nuevo fichero, pero he +olvidado hacerle \hgcmd{add}. +\interaction{rollback.commit} +La salida de \hgcmd{status} después de la consignación confirma +inmediatamente este error. +\interaction{rollback.status} +La consignación capturó los cambios en el fichero \filename{a}, pero +no el nuevo fichero \filename{b}. Si yo publicara este conjunto de +cambios a un repositorio compartido con un colega, es bastante +probable que algo en \filename{a} se refiriera a \filename{b}, el cual +podría no estar presente cuando jalen mis cambios del repositorio. Me +convertiría el sujeto de cierta indignación. + +Como sea, la suerte me acompaña---Encontré mi error antes de publicar +el conjunto de cambios. Uso la orden \hgcmd{rollback}, y Mercurial +hace desaparecer el último conjunto de cambios. +\interaction{rollback.rollback} +El conjunto de cambios ya no está en el historial del repositorio, y el +directorio de trabajo cree que el fichero \filename{a} ha sido +modificado. La consignación y el roll back dejaron el directorio de +trabajo exactamente como estaba antes de la consignación; el conjunto +de cambios ha sido eliminado totlamente. Ahora puedo hacer \hgcmd{add} +al fichero \filename{b}, y hacer de nuevo la consignación. +\interaction{rollback.add} + +\subsection{Erroneamente jalado} + +Mantener ramas de desarrollo separadas de un proyecto en distintos +repositorios es una práctica común con Mercurial. Su equipo de +desarrollo puede tener un repositorio compartido para la versión ``0.9'' +y otra con cambios distintos para la versión ``1.0''. + +Con este escenario, puede imaginar las consecuencias si tuviera un +repositorio local ``0.9'', y jalara accidentalmente los cambios del +repositorio compartido de la versión ``1.0'' en este. En el peor de +los casos, por falta de atención, es posible que publique tales +cambios en el árbol compartido ``0.9'', confundiendo a todo su equipo +de trabajo (pero no se preocupe, volveremos a este terrorífico +escenario posteriormente). En todo caso, es muy probable que usted se +de cuenta inmediatamente, dado que Mercurial mostrará el URL de donde +está jalando, o que vea jalando una sospechosa gran cantidad de +cambios en el repositorio. + +La orden \hgcmd{rollback} excluirá eficientemente los conjuntos de +cambios que haya acabado de jalar. Mercurial agrupa todos los cambios +de un \hgcmd{pull} a una única transacción y bastará con un +\hgcmd{rollback} para deshacer esta equivocación. + +\subsection{Después de publicar, un roll back es futil} +\label{sec:undo:rollback-after-push} + +El valor de \hgcmd{rollback} se anula cuando ha publicado sus cambios +a otro repositorio. Un cambio desaparece totalmente al hacer roll back, +pero \emph{solamente} en el repositorio en el cual aplica +\hgcmd{rollback}. Debido a que un roll back elimina el historial, +no hay forma de que la desaparición de un cambio se propague entre +repositorios. + +Si ha publicado un cambio en otro repositorio---particularmente si es +un repositorio público---esencialmente está ``en terreno agreste,'' +y tendrá que reparar la equivocación de un modo distinto. Lo que +pasará si publica un conjunto de cambios en algún sitio, hacer +rollback y después volver a jalar del repositorio del cual había +publicado, es que el conjunto de cambios reaparecerá en su repositorio. + +(Si está absolutamente segruro de que el conjunto de cambios al que +desea hacer rollback es el cambio más reciente del repositorio en el +cual publicó, \emph{y} sabe que nadie más pudo haber jalado de tal +repositorio, puede hacer rollback del conjunto de cambios allí, pero +es mejor no confiar en una solución de este estilo. Si lo hace, tarde +o temprano un conjunto de cambios logrará colarse en un repositorio +que usted no controle directamente (o del cual se ha olvidado), y +volverá a hostigarle.) + +\subsection{Solamente hay un roll back} + +Mercurial almacena exactamente una transacción en su bitácora de +transacciones; tal transacción es la más reciente de las que haya +ocurrido en el repositorio. Esto significa que solamente puede hacer +roll back a una transacción. Si espera poder hacer roll back a una +transacción después al antecesor, observará que no es el +comportamiento que obtendrá. +\interaction{rollback.twice} +Una vez que haya aplicado un rollback en una transacción a un +repositorio, no podrá volver a hacer rollback hasta que haga una +consignación o haya jalado. + +\section{Revertir un cambio equivocado} + +Si modifica un fichero y se da cuenta que no quería realmente cambiar +tal fichero, y todavía no ha consignado los cambios, la orden +necesaria es \hgcmd{revert}. Observa el conjunto de cambios padre del +directorio y restaura los contenidos del fichero al estado de tal +conjunto de cambios. (Es una forma larga de decirlo, usualmente +deshace sus modificaciones.) + +Ilustremos como actúa la orden \hgcmd{revert} con un ejemplo +pequeño. Comenzaremos modificando un fichero al cual Mercurial ya está +siguiendo. +\interaction{daily.revert.modify} +Si no queremos ese cambio, podemos aplicar \hgcmd{revert} al fichero. +\interaction{daily.revert.unmodify} +La orden \hgcmd{revert} nos brinda un grado adicional de seguridad +guardando nuestro fichero modificado con la extensión \filename{.orig}. +\interaction{daily.revert.status} + +Este es un resumen de casos en los cuales la orden \hgcmd{revert} es +de utilidad. Describiremos cada uno de ellos con más detalle en la +sección siguiente. +\begin{itemize} +\item Si usted modifica un fichero, lo restaurará a su estado sin + modificación previo. +\item Si usted hace \hgcmd{add} a un fichero, revertirá el estado de + ``adicionado'' del fichero, pero no lo tocará +\item Si borra un fichero sin decirle a Mercurial, restaurará el + fichero con sus contenidos sin modificación. +\item Si usa la orden \hgcmd{remove} para eliminar un fichero, deshará + el estado ``removido'' del fichero, y lo restaurará con sus + contenidos sin modificación. +\end{itemize} + +\subsection{Errores al administrar ficheros} +\label{sec:undo:mgmt} + +La orden \hgcmd{revert} es útil para más que ficheros modificados. Le +permite reversar los resultados de todas las órdenes de administración +de ficheros que provee Mercurial---\hgcmd{add}, \hgcmd{remove}, y las +demás. + +Si usted hace \hgcmd{add} a un fichero, y no deseaba que Mercurial le +diera seguimiento, use \hgcmd{revert} para deshacer la adición. No se +preocupe; Mercurial no modificará de forma alguna el fichero. +Solamente lo ``desmarcará''. +\interaction{daily.revert.add} + +De forma similar, Si le solicita a Mercurial hacer \hgcmd{remove} a un +fichero, puede usar \hgcmd{revert} para restarurarlo a los contenidos +que tenía la revisión padre del directorio de trabajo. +\interaction{daily.revert.remove} +Funciona de la misma manera para un fichero que usted haya eliminado +manualmente, sin decirle a Mercurial (recuerde que en la terminología +de Mercurial esta clase de fichero se llama ``faltante''). +\interaction{daily.revert.missing} + +Si usted revierte un \hgcmd{copy}, el fichero a donde se copió +permanece en su directorio de trabajo, pero sin seguimiento. Dado que +una copia no afecta el fichero fuente de copiado de ninguna maner, +Mercurial no hace nada con este. +\interaction{daily.revert.copy} + +\subsubsection{Un caso ligeramente especial:revertir un renombramiento} + +Si hace \hgcmd{rename} a un fichero, hay un detalle que debe tener en +cuenta. Cuando aplica \hgcmd{revert} a un cambio de nombre, no es +suficiente proveer el nombre del fichero destino, como puede verlo en +el siguiente ejemplo. +\interaction{daily.revert.rename} +Como puede ver en la salida de \hgcmd{status}, el fichero con el nuevo +nombre no se identifica más como agregado, pero el fichero con el +nombre-\emph{inicial} se elimna! Esto es contra-intuitivo (por lo +menos para mí), pero por lo menos es fácil arreglarlo. +\interaction{daily.revert.rename-orig} +Por lo tanto, recuerde, para revertir un \hgcmd{rename}, debe proveer +\emph{ambos} nombres, la fuente y el destino. + +% TODO: the output doesn't look like it will be removed! + +(A propósito, si elimina un fichero, y modifica el fichero con el +nuevo nombre, al revertir ambos componentes del renombramiento, cuando +Mercurial restaure el fichero que fue eliminado como parte del +renombramiento, no será modificado. +Si necesita que las modificaciones en el fichero destino del +renombramiento se muestren, no olvide copiarlas encima.) + +Estos aspectos engorrosos al revertir un renombramiento se constituyen +discutiblemente en un fallo de Mercurial. + +\section{Tratar cambios consignados} + +Considere un caso en el que ha consignado el cambio $a$, y otro cambio +$b$ sobre este; se ha dado cuenta que el cambio $a$ era +incorrecto. Mercurial le permite ``retroceder'' un conjunto de cambios +completo automáticamente, y construir bloques que le permitan revertir +parte de un conjunto de cambios a mano. + +Antes de leer esta sección, hay algo para tener en cuenta: la orden +\hgcmd{backout} deshace cambios \emph{adicionando} al historial, sin +modificar o borrar. Es la herramienta correcta si está arreglando +fallos, pero no si está tratando de deshacer algún cambio que tiene +consecuencias catastróficas. Para tratar con esos, vea la sección~\ref{sec:undo:aaaiiieee}. + +\subsection{Retroceder un conjunto de cambios} + +La orden \hgcmd{backout} le permite ``deshacer'' los efectos de todo +un conjunto de cambios de forma automatizada. Dado que el historial de +Mercurial es inmutable, esta orden \emph{no} se deshace del conjunto +de cambios que usted desea deshacer. En cambio, crea un nuevo +conjunto de cambios que \emph{reversa} el conjunto de cambios que +usted indique. + +La operación de la orden \hgcmd{backout} es un poco intrincada, y lo +ilustraremos con algunos ejemplos. Primero crearemos un repositorio +con algunos cambios sencillos. +\interaction{backout.init} + +La orden \hgcmd{backout} toma un ID de conjunto de cambios como su +argumento; el conjunto de cambios a retroceder. Normalmente +\hgcmd{backout} le ofrecerá un editor de texto para escribir el +mensaje de la consignación, para dejar un registro de por qué está +retrocediendo. En este ejemplo, colocamos un mensaje en la +consignación usando la opción \hgopt{backout}{-m}. + +\subsection{Retroceder el conjunto de cambios punta} + +Comenzamos retrocediendo el último conjunto de cambios que consignamos. +\interaction{backout.simple} +Puede ver que la segunda línea de \filename{myfile} ya no está +presente. La salida de \hgcmd{log} nos da una idea de lo que la orden +\hgcmd{backout} ha hecho. +\interaction{backout.simple.log} +Vea que el nuevo conjunto de cambios que \hgcmd{backout} ha creado es +un hijo del conjunto de cambios que retrocedimos. Es más sencillo de +ver en la figura~\ref{fig:undo:backout}, que presenta una vista +gráfica del historial de cambios. Como puede ver, el historial es +bonito y lineal. + +\begin{figure}[htb] + \centering + \grafix{undo-simple} + \caption{Retroceso de un cambio con la orden \hgcmd{backout}} + \label{fig:undo:backout} +\end{figure} + +\subsection{Retroceso de un cambio que no es la punta} + +Si desea retrocede un cambio distinto al último que ha consignado, use +la opción \hgopt{backout}{--merge} a la orden \hgcmd{backout}. +\interaction{backout.non-tip.clone} +Que resulta en un retroceso de un conjunto de cambios ``en un sólo +tiro'', una operación que resulta normalmente rápida y sencilla. +\interaction{backout.non-tip.backout} + +Si ve los contenidos del fichero \filename{myfile} después de +finalizar el retroceso, verá que el primer y el tercer cambio están +presentes, pero no el segundo. +\interaction{backout.non-tip.cat} + +Como lo muestra el historial gráfico en la +figura~\ref{fig:undo:backout-non-tip}, Mercurial realmente consigna +\emph{dos} cambios en estas situaciones (los nodos encerrados en una +caja son aquellos que Mercurial consigna automaticamente). Antes de +que Mercurial comience el proceso de retroceso, primero recuerda cuál +es el padre del directorio de trabajo. Posteriormente hace un +retroceso al conjunto de cambios objetivo y lo consigna como un +conjunto de cambios. Finalmente, fusiona con el padre anterior del +directorio de trabajo, y consigna el resultado de la fusión. + +% TODO: to me it looks like mercurial doesn't commit the second merge automatically! + +\begin{figure}[htb] + \centering + \grafix{undo-non-tip} + \caption{Retroceso automatizado de un cambio a algo que no es la punta con la orden \hgcmd{backout}} + \label{fig:undo:backout-non-tip} +\end{figure} + +El resultado es que usted termina ``donde estaba'', solamente con un +poco de historial adicional que deshace el efecto de un conjunto de +cambios que usted quería evitar. + +\subsubsection{Use siempre la opción \hgopt{backout}{--merge}} + +De hecho, dado que la opción \hgopt{backout}{--merge} siempre hara lo +``correcto'' esté o no retrocediendo el conjunto de cambios punta +(p.e.~no tratará de fusionar si está retrocediendo la punta, dado que +no es necesario), usted debería usar \emph{siempre} esta opción cuando +ejecuta la orden \hgcmd{backout}. + +\subsection{Más control sobre el proceso de retroceso} + +A pesar de que recomiendo usar siempre la opción +\hgopt{backout}{--merge} cuando está retrocediendo un cambio, la orden +\hgcmd{backout} le permite decidir cómo mezclar un retroceso de un +conjunto de cambios. Es muy extraño que usted necestite tomar control +del proceso de retroceso de forma manual, pero puede ser útil entender +lo que la orden \hgcmd{backout} está haciendo automáticamente para +usted. Para ilustrarlo, clonemos nuestro primer repositorio, pero +omitamos el retroceso que contiene. + +\interaction{backout.manual.clone} +Como en el ejemplo anterior, consignaremos un tercer cambio, después +haremos retroceso de su padre, y veremos qué pasa. +\interaction{backout.manual.backout} +Nuestro nuevo conjunto de cambios es de nuevo un descendiente del +conjunto de cambio que retrocedimos; es por lo tanto una nueva cabeza, +\emph{no} un descendiente del conjunto de cambios que era la punta. La +orden \hgcmd{backout} fue muy explícita diciéndolo. +\interaction{backout.manual.log} + +De nuevo, es más sencillo lo que pasó viendo una gráfica del +historial de revisiones, en la figura~\ref{fig:undo:backout-manual}. +Esto nos aclara que cuando usamos \hgcmd{backout} para retroceder un +cambio a algo que no sea la punta, Mercurial añade una nueva cabeza al +repositorio (el cambio que consignó está encerrado en una caja). + +\begin{figure}[htb] + \centering + \grafix{undo-manual} + \caption{Retroceso usando la orden \hgcmd{backout}} + \label{fig:undo:backout-manual} +\end{figure} + +Después de que la orden \hgcmd{backout} ha terminado, deja un nuevo +conjunto de cambios de ``retroceso'' como el padre del directorio de trabajo. +\interaction{backout.manual.parents} +Ahora tenemos dos conjuntos de cambios aislados. +\interaction{backout.manual.heads} + +Reflexionemos acerca de lo que esperamos ver como contenidos de +\filename{myfile}. El primer cambio debería estar presente, porque +nunca le hicimos retroceso. El segundo cambio debió desaparecer, +puesto que es el que retrocedimos. Dado que la gráfica del historial +muestra que el tercer camlio es una cabeza separada, \emph{no} +esperamos ver el tercer cambio presente en \filename{myfile}. +\interaction{backout.manual.cat} +Para que el tercer cambio esté en el fichero, hacemos una fusión usual +de las dos cabezas. +\interaction{backout.manual.merge} +Después de eso, el historial gráfica de nuestro repositorio luce como +la figura~\ref{fig:undo:backout-manual-merge}. + +\begin{figure}[htb] + \centering + \grafix{undo-manual-merge} + \caption{Fusión manual de un retroceso} + \label{fig:undo:backout-manual-merge} +\end{figure} + +\subsection{Por qué \hgcmd{backout} hace lo que hace} + +Esta es una descripción corta de cómo trabaja la orden \hgcmd{backout}. +\begin{enumerate} +\item Se asegura de que el directorio de trabajo es ``limpio'', esto + es, que la salida de \hgcmd{status} debería ser vacía. +\item Recuerda el padre actual del directorio de trabajo. A este + conjunto de cambio lo llamaremos \texttt{orig} +\item Hace el equivalente de un \hgcmd{update} para sincronizar el + directorio de trabajo con el conjunto de cambios que usted quiere + retroceder. Lo llamaremos \texttt{backout} +\item Encuentra el padre del conjunto de cambios. Lo llamaremos + \texttt{parent}. +\item Para cada fichero del conjunto de cambios que el + \texttt{retroceso} afecte, hará el equivalente a + \hgcmdargs{revert}{-r parent} sobre ese fichero, para restaurarlo a + los contenidos que tenía antes de que el conjunto de cambios fuera + consignado. +\item Se consigna el resultado como un nuevo conjunto de cambios y + tiene a \texttt{backout} como su padre. +\item Si especifica \hgopt{backout}{--merge} en la línea de comandos, + se fusiona con \texttt{orig}, y se consigna el resultado de la + fusión. +\end{enumerate} + +Una vía alternativa de implementar la orden \hgcmd{backout} sería usar +\hgcmd{export} sobre el conjunto de cambios a retroceder como un diff +y después usar laa opción \cmdopt{patch}{--reverse} de la orden +\command{patch} para reversar el efecto del cambio sin molestar el +directorio de trabajo. Suena mucho más simple, pero no funcionaría +bien ni de cerca. + +La razón por la cual \hgcmd{backout} hace una actualización, una +consignación, una fusión y otra consignación es para dar a la +maquinaria de fusión la mayor oportunidad de hacer un buen trabajo +cuando se trata con todos los cambios \emph{entre} el cambio que está +retrocediendo y la punta actual. + +Si está retrocediendo un conjunto de cambios que está a unas ~100 +atrás en su historial del proyecto, las posibilidades de que una orden +\command{patch} sea capaz de ser aplicada a un diff reverso, +claramente no son altas, porque los cambios que intervienen podrían +``no coincidir con el contexto'' que \command{patch} usa para +determinar si puede aplicar un parche (si esto suena como cháchara, +vea una discusión de la orden \command{patch} en \ref{sec:mq:patch}). +Adicionalmente, la maquinaria de fusión de Mercurial manejará ficheros +y directorios renombrados, cambios de permisos, y modificaciones a +ficheros binarios, nada de lo cual la orden \command{patch} puede manejar. + +\section{Cambios que nunca debieron ocurrir} +\label{sec:undo:aaaiiieee} + +En la mayoría de los casos, la orden \hgcmd{backout} es exactamente lo +que necesita para deshacer los efectos de un cambio. Deja un registro +permanente y exacto de lo que usted hizo, cuando se consignó el +conjunto de cambios original y cuando se hizo la limpieza. + +En ocasiones particulares, puede haber consignado un cambio que no +debería estar de ninguna forma en el repositorio. Por ejemplo, sería +muy inusual, y considerado como una equivocación, consignar los +ficheros objeto junto con el código fuente. Los ficheros objeto no +tienen valor intrínseco y son \emph{grandes}, por lo tanto aumentan el +tamaño del repositorio y la cantidad de tiempo que se emplea al clonar +o jalar cambios. + +Antes de discutir las opciones que tiene si consignó cambio del tipo +``bolsa de papel deschable'' (el tipo que es tan malo que le gustaría +colocarse una bolsa de papel desechable en su cabeza), permítame +discutir primero unas aproximaciones que probablemente no funcionen. + +Dado que Mercurial trata de forma acumulativa al historial---cada +cambio se coloca encima de todos los cambios que le +preceden---usualmente usted no puede hacer que unos cambios desastrosos +desaparezcan. La única excepción es cuando usted ha acabado de +consignar un cambio y este no ha sido publicado o jalado en otro +repositorio. Ahí es cuando puede usar la orden \hgcmd{rollback} con +seguridad, como detallé en la sección~\ref{sec:undo:rollback}. + +Después de que usted haya publicado un cambio en otro repositorio, usted +\emph{podría} usar la orden \hgcmd{rollback} para hacer que en su copia +local desaparezca el cambio, pero no tendrá las consecuencias que +desea. El cambio estará presente en un repositorio remoto, y +reaparecerá en su repositorio local la próxima vez que jale + +Si una situación como esta se presenta, y usted sabe en qué +repositorios su mal cambio se ha propagado, puede \emph{intentar} +deshacerse del conjunto de cambios de \emph{todos} los repositorios en +los que se pueda encontrar. Esta por supuesto, no es una solución +satisfactoria: si usted deja de hacerlo en un solo repositorio, +mientras esté eliminándolo, el cambio todavía estará ``allí afuera'', +y podría propagarse más tarde. + +Si ha consignado uno o más cambios \emph{después} del cambio que desea +desaparecer, sus opciones son aún más reducidas. Mercurial no provee +una forma de ``cabar un hueco'' en el historial, dejando los conjuntos +de cambios intactos. + +%Dejamos de traducir lo que viene a continuación, porque será +%modificado por upstream... + +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{Cómo protegerse de cambios que han ``escapado''} + +Si ha consignado cambios a su repositorio local y estos han sido +publicados o jalados en cualquier otro sitio, no es necesariamente un +desastre. Puede protegerse de antemano de ciertas clases de conjuntos +de cambios malos. Esto es particularmente sencillo si su equipo de +trabajo jala cambios de un repositorio central. + +Al configurar algunos ganchos en el repositorio central para validar +conjuntos de cambios (ver capítulo~\ref{chap:hook}), puede prevenir la +publicación automáticamente de cierta clase de cambios malos. Con tal +configuración, cierta clase de conjuntos de cambios malos tenderán +naturalmente a``morir'' debido a que no pueden propagarse al +repositorio central. Esto sucederá sin necesidad de intervención +explícita. + +Por ejemplo, un gancho de cambios de entrada que verifique que un +conjunto de cambios compila, puede prevenir que la gente ``rompa +la compilación'' inadvertidamente. + +\section{Al encuentro de la fuente de un fallo} +\label{sec:undo:bisect} + +Aunque es muy bueno poder retroceder el conjunto de cambios que +originó un fallo, se requiere que usted sepa cual conjunto de cambios +retroceder. Mercurial brinda una orden invaluable, llamada +\hgcmd{bisect}, que ayuda a automatizar este proceso y a alcanzarlo +muy eficientemente. + +La idea tras la orden \hgcmd{bisect} es que el conjunto de cambios que +ha introducido un cambio de comportamiento pueda identificarse con una +prueba binaria sencilla. No tiene que saber qué pieza de código +introdujo el cambio, pero si requiere que sepa cómo probar la +existencia de un fallo. La orden \hgcmd{bisect} usa su prueba para +dirigir su búsqueda del conjunto de cambios que introdujo el código +causante del fallo. + +A continuación un conjunto de escenarios que puede ayudarle a entender +cómo puede aplicar esta orden. +\begin{itemize} +\item La versión más reciente de su programa tiene un fallo que usted + recuerda no estaba hace unas semanas, pero no sabe cuándo fue + introducido. En este caso, su prueba binaria busca la presencia de + tal fallo. +\item Usted arregló un fallo en un apurto, y es hora de dar por + cerrado el caso en la base de datos de fallos de su equipo de + trabajo. La base de datos de fallos requiere el ID del conjunto de + cambios que permita dar por cerrado el caso, pero usted no recuerda + qué conjunto de cambios arregló tal fallo. De nuevo la prueba + binaria revisa la presencia del fallo. +\item Su programa funciona correctamente, pero core ~15\% más lento + que la última vez que lo midió. Usted desea saber qué conjunto de + cambios introdujo esta disminución de desempeño. En este caso su + prueba binaria mide el desempeño de su programa, para ver dónde es + ``rápido'' y dónde es ``lento''. +\item Los tamaños de los componentes del proyecto que usted lleva se + expandieron recientemente, y sospecha que algo cambio en la forma en + que se construye su proyecto. +\end{itemize} + +Para estos ejemplos debería ser claro que la orden \hgcmd{bisect} +es útil no solamente para encontrar la fuente de los fallos. Puede +usarla para encontrar cualquier ``propiedad emergente'' de un +repositorio (Cualquier cosa que usted no pueda encontrar con una +búsqueda de texto sencilla sobre los ficheros en el árbol) para la +cual pueda escribir una prueba binaria. + +A continuación introduciremos algo terminología, para aclarar qué +partes del proceso de búsqueda son su responsabilidad y cuáles de +Mercurial. Una \emph{prueba} es algo que \emph{usted} ejecuta cuando +\hgcmd{bisect} elige un conjunto de cambios. Un \emph{sondeo} es lo que +\hgcmd{bisect} ejecuta para decidir si una revisión es buena. Finalmente, +usaremos la palabra ``biseccionar', en frases como ``buscar con la +orden \hgcmd{bisect}''. + +Una forma sencilla de automatizar el proceso de búsqueda sería probar +cada conjunto de cambios. Lo cual escala muy poco. Si le tomó diez +minutos hacer pruebas sobre un conjunto de cambios y tiene 10.000 +conjuntos de cambios en su repositorio, esta aproximación exhaustiva +tomaría en promedio~35 \emph{días} para encontrar el conjunto de +cambios que introdujo el fallo. Incluso si supiera que el fallo se +introdujo en un de los últimos 500 conjuntos de cambios y limitara la +búsqueda a ellos, estaría tomabdi más de 40 horas para encontrar al +conjunto de cambios culpable. + +La orden \hgcmd{bisect} usa su conocimiento de la ``forma'' del +historial de revisiones de su proyecto para hacer una búsqueda +proporcional al \emph{logaritmo} del número de conjunto de cambios a +revisar (el tipo de búsqueda que realiza se llama búsqueda +binaria). Con esta aproximación, el buscar entre 10.000 conjuntos de +cambios tomará menos de 3 horas, incluso a diez minutos por prueba (La +búsqueda requerirá cerca de 14 pruebas). Al limitar la búsqueda a la +última centena de conjuntos de cambios, tomará a lo sumo una +hora (Apenas unas 7 pruebas). + +La orden \hgcmd{bisect} tiene en cuenta la naturaleza ``ramificada'' +del historial de revisiones del proyecto con Mercurial, así que no +hay problemas al tratar con ramas, fusiones o cabezas múltiples en un +repositorio. Puede evitar ramas enteras de historial con un solo +sondeo. + +\subsection{Uso de la orden \hgcmd{bisect}} + +A continuación un ejemplo de \hgcmd{bisect} en acción. + +\begin{note} + En las versiones 0.9.5 y anteriores de Mercurial, \hgcmd{bisect} no + era una orden incluída en la distribución principal: se ofrecía como + una extensión de Mercurial. Esta sección describe la orden embebida + y no la extensión anterior. +\end{note} + +Creamos un repostorio para probar el comando \hgcmd{bisect} de forma +aislada +\interaction{bisect.init} +Simularemos de forma sencilla un proyecto con un fallo: haremos +cambios triviales en un ciclo, e indicaremos que un cambio específico +sea el ``fallo''. Este ciclo crea 35 conjuntos de cambios, cada uno +añade un único fichero al repositorio. Representaremos nuestro ``fallo'' +con un fichero que contiene el texto ``tengo un gub''. +\interaction{bisect.commits} + +A continuación observaremos cómo usar la orden \hgcmd{bisect}. Podemos +usar el mecanismo de ayuda embebida que trae Mercurial. +\interaction{bisect.help} + +La orden \hgcmd{bisect} trabaja en etapas, de la siguiente forma: +\begin{enumerate} +\item Usted ejecuta una prueba binaria. + \begin{itemize} + \item Si la prueba es exitosa, usted se lo indicará a \hgcmd{bisect} + ejecutando la orden \hgcmdargs{bisect}{good}. + \item Si falla, ejecutará la orden \hgcmdargs{bisect}{--bad}. + \end{itemize} +\item La orden usa su información para decidir qué conjuntos de + cambios deben probarse a continuación. +\item Actualiza el directorio de trabajo a tal conjunto de cambios y + el proceso se lleva a cabo de nuevo. +\end{enumerate} +El proceso termina cuando \hgcmd{bisect} identifica un único conjunto +de cambios que marca el punto donde se encontró la transición de +``exitoso'' a ``fallido''. + +Para comenzar la búsqueda, es indispensable ejecutar la orden +\hgcmdargs{bisect}{--reset}. +\interaction{bisect.search.init} + +En nuestro caso, la prueba binaria es sencilla: revisamos si el +fichero en el repositorio contiene la cadena ``tengo un gub''. Si la +tiene, este conjunto de cambios contiene aquel que ``causó el fallo''. +Por convención, un conjunto de cambios que tiene la propiedad que +estamos buscando es ``malo'', mientras que el otro que no la tiene es +``bueno''. + +En la mayoría de casos, la revisión del directorio actual (usualmente +la punta) exhibe el problema introducido por el cambio con el fallo, +por lo tanto la marcaremos como ``mala''. +\interaction{bisect.search.bad-init} + +Nuestra próxima tarea es nominar al conjunto de cambios que sabemos +\emph{no} tiene el fallo; la orden \hgcmd{bisect} ``acotará'' su +búsqueda entre el primer par de conjuntos de cambios buenos y malos. +En nuestro caso, sabemos que la revisión~10 no tenía el fallo. (Más +adelante diré un poco más acerca de la elección del conjunto de +cambios ``bueno''.) +\interaction{bisect.search.good-init} + +Note que esta orden mostró algo. +\begin{itemize} +\item Nos dijo cuántos conjuntos de cambios debe considerar antes de + que pueda identifica aquel que introdujo el fallo, y cuántas pruebas + se requerirán. +\item Actualizó el directorio de trabajo al siguiente conjunto de + cambios, y nos dijo qué conjunto de cambios está evaluando. +\end{itemize} + +Ahora ejecutamos nuestra prueba en el directorio de trabajo. Usamos la +orden \command{grep} para ver si nuestro fichero ``malo'' está +presente en el directorio de trabajo. Si lo está, esta revisión es +mala; si no esta revisión es buena. +\interaction{bisect.search.step1} + +Esta prueba luce como candidata perfecta para automatizarse, por lo +tanto la convertimos en una función de interfaz de comandos. +\interaction{bisect.search.mytest} +Ahora podemos ejecutar un paso entero de pruebas con un solo comando, +\texttt{mytest}. +\interaction{bisect.search.step2} +Unas invocaciones más de nuestra prueba, y hemos terminado. +\interaction{bisect.search.rest} + +Aunque teníamos unos~40 conjuntos de cambios en los cuales buscar, la +orden \hgcmd{bisect} nos permitió encontrar el conjunto de cambios que +introdujo el ``fallo'' con sólo cinco pruebas. Porque el número de +pruebas que la orden \hgcmd{bisect} ejecuta crece logarítmicamente con +la cantidad de conjuntos de cambios a buscar, la ventaja que esto +tiene frente a la búsqueda por``fuerza bruta'' crece con cada +conjunto de cambios que usted adicione. + +\subsection{Limpieza después de la búsqueda} + +Cuando haya terminado de usar la orden \hgcmd{bisect} en un +repositorio, puede usar la orden \hgcmdargs{bisect}{reset} para +deshacerse de la información que se estaba usando para lograr la +búsqueda. Lar orden no usa mucho espacio, así que no hay problema si +olvida ejecutar la orden. En todo caso, \hgcmd{bisect} no le +permitirá comenzar una nueva búsqueda sobre el repositorio hasta que +no aplique \hgcmdargs{bisect}{reset}. +\interaction{bisect.search.reset} + +\section{Consejos para encontrar fallos efectivamente} + +\subsection{Dar una entrada consistente} + +La orden \hgcmd{bisect} requiere que usted ofrezca un reporte correcto +del resultado de cada prueba que aplique. Si usted le dice que una +prueba falla cuando en realidad era acertada, \emph{podría} detectar +la inconsistencia. Si puede identificar una inconsistencia en sus +reportes, le dirá que un conjunto de cambios particular es a la vez +bueno y malo. Aunque puede no hacerlo; estaría tratando de reportar +un conjunto de cambios como el responsable de un fallo aunque no lo +sea. + +\subsection{Automatizar tanto como se pueda} + +Cuando comencé a usar la orden \hgcmd{bisect}, intenté ejecutar +algunas veces las pruebas a mano desde la línea de comandos. Es una +aproximación a la cual no esta acostumbrado. Después de algunos +intentos, me di cuenta que estaba cometiendo tantas equivocaciones que +tenía que comenzar de nuevo con mis búsquedas varias veces antes de +llegar a los resultados deseados. + +Mi problema inicial al dirigir a la orden \hgcmd{bisect} manualmente +ocurrieron incluso con búsquedas en repositorios pequeños; si el +problema que está buscando es más sutil, o el número de pruebas que +\hgcmd{bisect} debe aplicar, la posibilidad de errar es mucho más +alta. Una vez que comencé a automatizar mis pruebas, obtuve mejores +resultados. + +La clave para las pruebas automatizadas se puede resumir en: +\begin{itemize} +\item pruebe siempre buscando el mismo síntoma y +\item ofrezca siempre datos consistentes a la orden \hgcmd{bisect}. +\end{itemize} +En mi tutorial de ejemplo anterior, la orden \command{grep} busca el +síntoma, y la construcción \texttt{if} toma el resultado de esta +prueba y verifica que siempre alimentamos con los mismos datos a la +orden \hgcmd{bisect}. La función \texttt{mytest} los une de una forma +reproducible, logrando que cada prueba sea uniforme y consistente. + +\subsection{Verificar los resultados} + +Dado que la salida de la búsqueda de \hgcmd{bisect} es tan buena como +los datos ofrecidos por usted, no confíe en esto como si fuera la +verdad absoluta. Una forma sencilla de asegurarse es ejecutar +manualmente su prueba a cada uno de los siguientes conjuntos de +cambios: +\begin{itemize} +\item El conjunto de cambios que se reportó como la primera versión + erronea. Su prueba debería dar un reporte de fallo. +\item El conjunto de cambios padre (cada padre, si es una fusión). + Su prueba debería reportar este(os) conjunto(s) de cambios como + bueno(s). +\item Un hijo del conjunto de cambios. Su prueba debería reportar al + conjunto de cambios hijo como malo. +\end{itemize} + +\subsection{Tener en cuenta la interferencia entre fallos} + +Es posible que su búsqueda de un fallo pueda viciarse por la presencia +de otro. Por ejemplo, digamos que su programa se revienta en la +revisión 100, y que funcionó correctamente en la revisión 50. Sin su +conocimiento, alguien introdujo un fallo con consecuencias grandes en +la revisión 60, y lo arregló en la revisión 80. Sus resultados +estarían distorcionados de una o muchas formas. + +Es posible que este fallo ``enmascare'' completamente al suyo, y que +podría haberse revelado antes de que su propio fallo haya tenido +oportunidad de manifestarse. Si no puede saltar el otro fallo (por +ejemplo, este evita que su proyecto se arme o compile), y de esta +forma no se pueda revisar si su fallo esté presente en un conjunto +particular de cambios, la orden \hgcmd{bisect} no podrá ayudarle +directamente. En cambio, puede marcar este conjunto de cambios como +al ejecutar \hgcmdargs{bisect}{--skip}. + +Un problema distinto podría surgir si su prueba de la presencia de un +fallo no es suficientemente específica. Si usted busca ``mi programa +se revienta'', entonces tanto su fallo como el otro fallo sin relación +que terminan presentando síntomas distintos, podría terminar +confundiendo a \hgcmd{bisect}. + +Otra situación en la cual sería de mucha utilidad emplear a +\hgcmdargs{bisect}{--skip} surge cuando usted no puede probar una +revisión porque su proyecto estaba en una situación de rompimiento y +por lo tanto en un estado en el cual era imposible hacer la prueba en +esa revisión, tal vez porque alguien consignó un cambio que hacía +imposible la construcción del proyecto. + +\subsection{Acotar la búsqueda perezosamente} + +Elegir los primeros ``buenos'' y ``malos'' conjuntos de cambios que +marcarán los límites de su búsqueda en general es sencillo, pero vale +la pena discutirlo. Desde la perspectiva de \hgcmd{bisect}, el +conjunto de cambios ``más nuevo'' por convención es el ``malo'', y el +otro conjunto de cambios es el ``bueno''. + +Si no recuerda cuál podría ser el cambio ``bueno'', para informar a +\hgcmd{bisect}, podría hacer pruebas aleatorias en el peor de los +casos. Pero recuerde eliminar aquellos conjuntos de cambios que +podrían no exhibir el fallo (tal vez porque la característica donde se +presenta el fallo todavía no está presente) y aquellos en los cuales +otro fallo puede enmascararlo (como se discutió anteriormente). + +Incluso si termina ``muy atrás'' por miles de conjuntos de cambios o +meses de historial, solamente estaŕa adicionando unas pruebas contadas +para \hgcmd{bisect}, gracias al comportamiento logarítmico. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 5981a0f7540a -r 019040fbf5f5 es/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-after-commit.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + dfbbb33f3fa3 + + + e7639888bb2f + + 7b064d8bac5e + + + + 000000000000 + + Historia en el repositorio + + + + dfbbb33f3fa3 + + + + 000000000000 + + Primer padre + Segundo padre + Padres del directorio de trabajo + + + Nuevoconjuntodecambios + + diff -r 5981a0f7540a -r 019040fbf5f5 es/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-branch.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + 000000000000 + + Primer padre + Segundo padre + Padres del directorio de trabajo + + + + + ffb20e1701ea + + + Cabeza Pre-existente + Cabeza recién creada (y tip) + + + + diff -r 5981a0f7540a -r 019040fbf5f5 es/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-merge.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + e7639888bb2f + + Primer padre (sin cambio) + Segundo padre + Padres del directorio de trabajo + + + + + ffb20e1701ea + + + Cabeza pre-existente + Cabeza recién creada(y tip) + + + + + + e7639888bb2f + + + diff -r 5981a0f7540a -r 019040fbf5f5 es/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-pre-branch.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + e7639888bb2f + + + 7b064d8bac5e + + + + 000000000000 + + Historia en el repositorio + + + + 7b064d8bac5e + + + + 000000000000 + + Primer padre + Segundo padre + Padres del directorio de trabajo + + + + diff -r 5981a0f7540a -r 019040fbf5f5 es/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir.svg Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + Historia en el repositorio + + + + + e7639888bb2f + + + + 000000000000 + + Primer padre + Segundo padre + + Padres del directorio de trabajo + + + + diff -r 5981a0f7540a -r 019040fbf5f5 examples/hg-interdiff --- a/examples/hg-interdiff Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -# Adapter for using interdiff with mercurial's extdiff extension. -# -# Copyright 2006 Bryan O'Sullivan -# -# This software may be used and distributed according to the terms of -# the GNU General Public License, incorporated herein by reference. - -import os, sys - -def walk(base): - # yield all non-directories below the base path. - for root, dirs, files in os.walk(base): - for f in files: - path = os.path.join(root, f) - yield path[len(base)+1:], path - else: - if os.path.isfile(base): - yield '', base - -# create list of unique file names under both directories. -files = dict(walk(sys.argv[1])) -files.update(walk(sys.argv[2])) -files = files.keys() -files.sort() - -def name(base, f): - if f: - path = os.path.join(base, f) - else: - path = base - # interdiff requires two files; use /dev/null if one is missing. - if os.path.exists(path): - return path - return '/dev/null' - -ret = 0 - -for f in files: - if os.system('interdiff "%s" "%s"' % (name(sys.argv[1], f), - name(sys.argv[2], f))): - ret = 1 - -sys.exit(ret) diff -r 5981a0f7540a -r 019040fbf5f5 examples/hg-replay --- a/examples/hg-replay Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -# Adapter for using interdiff with mercurial's extdiff extension. -# -# Copyright 2006 Bryan O'Sullivan -# -# This software may be used and distributed according to the terms of -# the GNU General Public License, incorporated herein by reference. - -import os -import shutil -import sys -import tempfile - -if len(sys.argv) < 4: - print >> sys.stderr, ('usage: %s srcrepo destrepo cset-to-omit [...]' % - os.path.basename(sys.argv[0])) - sys.exit(1) - -srcrepo, destrepo = sys.argv[1], sys.argv[2] -omit = sys.argv[3:] - -changemap = {} -revs = [] - -parent = None - -sys.stdout.write('gathering history...') -sys.stdout.flush() - -for line in os.popen("hg --cwd %r log -r0:tip --template '{rev}:{node} {parents}\n'" % srcrepo): - changes = line.split() - cset = changes[0].split(':')[1] - rev = len(revs) - changemap[cset] = rev - if len(changes) >= 2: - p1 = int(changes[1].split(':', 1)[0]) - if len(changes) == 3: - p2 = int(changes[2].split(':', 1)[0]) - else: - p2 = None - if len(changes) == 1: - p1 = parent - revs.append((cset, p1, p2)) - parent = rev - -sys.stdout.write(' %d revs\n' % len(revs)) - -def findrev(r): - try: - i = int(r) - if str(i) == r: - rev = i - if rev < 0: - rev += len(revs) - if rev < 0 or rev > len(revs): - print >> sys.stderr, 'bad changeset: %r' % r - sys.exit(1) - cset = revs[rev][0] - except ValueError: - cset = r - matches = [changemap[c] for c in changemap if c.startswith(cset)] - if len(matches) != 1: - print >> sys.stderr, 'bad changeset: %r' % r - sys.exit(1) - rev = matches[0] - return rev - -def run(cmd): - print cmd - ret = os.system(cmd) - if ret: - print >> sys.stderr, 'failure:', cmd - sys.exit(1) - -omit = map(findrev, omit) -omit.sort() -newrevs = revs[:omit[0]] -tip = len(newrevs) - 1 -run('hg clone -q -r%s %r %r' % (tip, srcrepo, destrepo)) - -os.environ['HGMERGE'] = 'true' - -patchdir = tempfile.mkdtemp(prefix='replay.') -try: - run('hg --cwd %r export --git -o %r%s%%R %d:tip' % - (srcrepo, patchdir, os.sep, omit[0]+1)) - for rev in xrange(omit[0], len(revs)): - if rev in omit: - print 'omit', rev - newrevs.append((None, revs[rev][1], None)) - continue - _, p1, p2 = revs[rev] - np1 = newrevs[p1][1] - if tip != np1: - run('hg --cwd %r update -q -C %s' % (destrepo, np1)) - np2 = None - if p2: - np2 = newrevs[p2][1] - run('hg --cwd %r merge -q %s' % (destrepo, np2)) - print >> sys.stderr, 'XXX - cannot handle merges properly yet' - run('hg --cwd %r import -q -f %r%s%d' % (destrepo, patchdir, os.sep, rev)) - tip = len(newrevs) - 1 - newrevs.append((None, tip, np2)) -finally: - print 'cleaning up ...' - #shutil.rmtree(patchdir) diff -r 5981a0f7540a -r 019040fbf5f5 htdocs/hgicon.png Binary file htdocs/hgicon.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 htdocs/index.en.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/htdocs/index.en.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,51 @@ + + + + + + + Distributed revision control with Mercurial + + + +

    Distributed revision control with Mercurial

    + +

    Welcome to the home of the book “Distributed revision + control with Mercurial”, by Bryan O'Sullivan. + This is a freely licensed book + about the Mercurial revision + control system.

    + + + +

    How you can help Mercurial, and help free software

    + +

    Mercurial is a member of the Software + Freedom Conservancy, a wonderful non-profit + organisation that offers its member projects legal and + administrative advice. The SFC can accept accept + donations (tax-free under IRS 501(c)(3), within the United + States) on behalf of its member projects. If you would like to + support Mercurial directly, please consider making a donation to + the SFC on its behalf.

    + +

    If you would like to help free software developers to provide + their important public services without being impeded by legal + issues, please consider donating to the SFC's sister + organisation, the Software Freedom Law + Center.

    + + diff -r 5981a0f7540a -r 019040fbf5f5 htdocs/index.es.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/htdocs/index.es.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,53 @@ + + + + + + + Control Distribuido de Revisiones con Mercurial + + + +

    Control Distribuido de Revisiones con Mercurial

    + +

    Bienvenido al sito del libro “Control Distribuido de Revisiones con Mercurial”, en español, + por Bryan O'Sullivan. + Este libro está cobijado por una licencia abierta + y trata del sistema de control de revisiones + Mercurial. + +

    Los traductores son Javier Rojas e + Igor Támara. En este sitio usted puede encontrar: +

    + Para más detalles acerca del proceso de traducción, por favor vea este + fichero. + +

    ¿Cómo puede usted ayudar a Mercurial, y el software libre?

    + +

    Mercurial es miembro del Conservatorio + de Software Libre, una maravillosa organización sin ánimo + de lucro que ofrece a sus proyectos miembros consejo legal y + administrativo. La SFC acepta donaciones + (deducibles de impuestos bajo IRS 501(c)(3), dentro de los Estados Unidos) + en representación de sus proyectos miembros. Si desea dar un apoyo + directo a Mercurial, por favor considere hacer una donación a SFC + en su representación.

    + +

    Si desea apoyar a los desarrolladores de software libre en su + importante servicio público sin estar impedido por cuestiones + legales, por favor considere donar a la organización hermana de + SFC, el Centro de Leyes de Software + Libre.

    + + diff -r 5981a0f7540a -r 019040fbf5f5 htdocs/index.html.var --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/htdocs/index.html.var Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,7 @@ +URI: index.en.html +Content-Language: en +Content-Type: text/html; charset=UTF-8 + +URI: index.es.html +Content-Language: es +Content-Type: text/html; charset=UTF-8 diff -r 5981a0f7540a -r 019040fbf5f5 html/hgicon.png Binary file html/hgicon.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 html/index.en.html --- a/html/index.en.html Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ - - - - - - - Distributed revision control with Mercurial - - - -

    Distributed revision control with Mercurial

    - -

    Welcome to the home of the book “Distributed revision - control with Mercurial”, by Bryan O'Sullivan. - This is a freely licensed book - about the Mercurial revision - control system.

    - - - -

    How you can help Mercurial, and help free software

    - -

    Mercurial is a member of the Software - Freedom Conservancy, a wonderful non-profit - organisation that offers its member projects legal and - administrative advice. The SFC can accept accept - donations (tax-free under IRS 501(c)(3), within the United - States) on behalf of its member projects. If you would like to - support Mercurial directly, please consider making a donation to - the SFC on its behalf.

    - -

    If you would like to help free software developers to provide - their important public services without being impeded by legal - issues, please consider donating to the SFC's sister - organisation, the Software Freedom Law - Center.

    - - diff -r 5981a0f7540a -r 019040fbf5f5 html/index.html.var --- a/html/index.html.var Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -URI: index.en.html -Content-Language: en -Content-Type: text/html; charset=UTF-8 diff -r 5981a0f7540a -r 019040fbf5f5 ja/Makefile --- a/ja/Makefile Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/Makefile Tue Apr 21 00:36:40 2009 +0900 @@ -1,64 +1,26 @@ # 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 := $(wildcard figs/*.dot figs/*.gif figs/*.png figs/*.svg) -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 +xml-src-files := \ + 00book.xml \ + app*.xml \ + ch*.xml image-dot := $(filter %.dot,$(image-sources)) image-svg := $(filter %.svg,$(image-sources)) -image-png := $(filter %.png,$(image-sources)) +image-oth := $(filter %.gif %.png,$(image-sources)) + +obj-web := html +obj-websup := $(obj-web)/support +obj-web-read := $(obj-web)/read -image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) -image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) -#image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png) -image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png:%.png=%.eps) +image-web := \ + $(image-dot:%.dot=$(obj-web-read)/%.png) \ + $(image-svg:%.svg=$(obj-web-read)/%.png) \ + $(image-oth:%=$(obj-web-read)/%) -example-sources := \ +example-sources-by-name := \ backout \ bisect \ branching \ @@ -90,6 +52,49 @@ tour \ tour-merge-conflict +example-sources := \ + $(example-sources-by-name:%=examples/%) \ + $(wildcard examples/ch*/*) + +extras-web-base := \ + $(obj-web)/index.html \ + $(obj-web)/robots.txt \ + $(obj-websup)/form-min.js \ + $(obj-websup)/form.js \ + $(obj-websup)/hsbook.js \ + $(obj-websup)/jquery-min.js \ + $(obj-websup)/jquery.js \ + $(obj-websup)/styles.css + +extras-web := $(extras-web-base) $(extras-web-base:%=%.gz) + +xsltproc := xsltproc +xsltproc-opts := --nonet --xinclude --path '$(xml-path)' + +xmllint := xmllint +xmllint-opts := --noout --nonet --valid + +system-xsl-dir := $(firstword $(wildcard \ + /usr/share/sgml/docbook/xsl-stylesheets \ + /usr/share/xml/docbook/stylesheet/nwalsh \ + )) + +# Bletcherousness. + +ifneq ($(wildcard /usr/share/sgml/docbook/xml-dtd-4.4-*),) +dtd-dir := $(wildcard /usr/share/sgml/docbook/xml-dtd-4.4-*) +else +ifneq ($(wildcard /usr/share/xml/docbook/schema/dtd/4.4),) +dtd-dir := $(wildcard /usr/share/xml/docbook/schema/dtd/4.4) +else +$(error Do not know where to look for DocBook XML 4.4 DTD) +endif +endif + +ifeq ($(system-xsl-dir),) +$(error add a suitable directory to system-xsl-dir) +endif + example-prereqs := \ /usr/bin/merge @@ -98,11 +103,6 @@ ../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') @@ -110,132 +110,111 @@ hg-version = $(shell hg version -q | \ sed 's,.*(version \(unknown\|[a-f0-9+]*\)),\1,') -all: dvi - -#dvi: $(sources) $(image-eps) examples -dvi: $(sources) $(image-eps) - platex 00book.tex +all: web complete.xml - cp 00book.aux hgbook.aux - bibtex hgbook +../stylesheets/system-xsl: $(system-xsl-dir) + ln -s $< $@ - platex 00book.tex - platex 00book.tex - platex 00book.tex +web: ../stylesheets/system-xsl websup html - - - +html: $(obj-web-read)/index.html - -pdf: pdf/hgbook.pdf - -define pdf - mkdir -p $(dir $@) - TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) +../web/index-read.html.in: ../web/genindex.py $(xml-src-files) + cd ../web && ./genindex.py - 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 +$(obj-web-read)/index.html: ../stylesheets/system-xsl .validated-00book.xml ../web/index-read.html.in + xsltproc $(xsltproc-opts) -o $(obj-web-read)/x ../stylesheets/chunk-stylesheet.xsl 00book.xml + python ../web/texpand.py ../web/index-read.html.in html/read/index.html + for i in $(obj-web-read)/*.html; do \ + gzip -9 -c $$i > $$i.gz; \ + done -#pdf/hgbook.pdf: $(sources) $(image-pdf) examples -pdf/hgbook.pdf: $(sources) $(image-pdf) - $(call pdf) - -html: onepage split - -onepage: $(htlatex) html/onepage/hgbook.html html/onepage/hgbook.css $(image-html:%=html/onepage/%) +websup: $(extras-web) $(image-web) + mkdir -p $(obj-websup)/figs $(obj-web-read)/figs + cp ../stylesheets/system-xsl/images/*.png $(obj-websup)/figs + cp -f ../web/icons/*.png $(obj-websup)/figs -html/onepage/%: % - cp $< $@ - -split: $(htlatex) html/split/hgbook.html html/split/hgbook.css $(image-html:%=html/split/%) - -html/split/%: % - cp $< $@ +complete.xml: .validated-00book.xml + $(xsltproc) $(xsltproc-opts) -o $@ ../stylesheets/dtd-profile.xsl 00book.xml -# 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. +all-ids.dat: ../stylesheets/all-ids.xsl $(xml-src-files) + $(xsltproc) $(xsltproc-opts) -o $@ ../stylesheets/all-ids.xsl 00book.xml + +web: websup -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 +valid: .validated-00book.xml -#html/onepage/hgbook.html: $(sources) $(image-html) examples bookhtml.cfg -html/onepage/hgbook.html: $(sources) $(image-html) bookhtml.cfg - $(call htlatex,$@,$<) - -#html/split/hgbook.html: $(sources) examples bookhtml.cfg -html/split/hgbook.html: $(sources) bookhtml.cfg - $(call htlatex,$@,$<,2) +.validated-00book.xml: $(xml-src-files) examples/.run + $(xmllint) --path '$(dtd-dir):$(xml-path)' $(xmllint-opts) $< + touch $@ # Produce 90dpi PNGs for the web. -%.png: %.svg - inkscape -D -e $@ $< - -%.svg: %.dot - dot -Tsvg -o $@ $< - -# Produce eps & pdf for the pdf +$(obj-web-read)/figs/%.png: $(obj-web-read)/figs/%.svg fixsvg + mkdir -p $(dir $@) + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg -%.pdf: %.eps - epstopdf $< - -%.eps: %.svg - inkscape -E $@ $< +$(obj-web-read)/figs/%.png: figs/%.svg fixsvg + mkdir -p $(dir $@) + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg -%.eps: %.dot - dot -Tps -o $@ $< +$(obj-web-read)/figs/%.gif: figs/%.gif + cp $< $@ -%.eps: %.png - convert $< ps:$@ +$(obj-web-read)/figs/%.png: figs/%.png + cp $< $@ + +$(obj-web-read)/figs/%.svg: figs/%.dot + mkdir -p $(dir $@) + dot -Tsvg -o $@ $< examples: $(example-prereqs) examples/.run -examples/.run: $(example-sources:%=examples/%.run) - touch examples/.run +examples/.run: $(example-sources) + cd examples && ./run-example -a 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 + -rm -rf dist html $(image-dot:%.dot=%.pdf) $(image-dot:%.dot=%.png) \ + $(image-svg:%.svg=%.png) examples/*.{lxo,run} examples/.run -install: pdf split $(dist-sources) +install: html $(dist-sources) rm -rf dist mkdir -p dist - cp pdf/hgbook.pdf dist - cp html/split/*.{css,html,png} dist + cp html/*.{css,html,png} dist cp $(dist-sources) dist +rsync: install + rsync -avz --delete dist sp.red-bean.com:public_html/hgbook + +vpath %.css ../web +vpath %.html.in ../web +vpath %.js ../web/javascript + +$(obj-websup)/%.css: %.css + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-websup)/%.jpg: %.jpg + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-websup)/%.js: %.js + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-web)/%: ../web/% + @mkdir -p $(dir $@) + cp $< $@ + +$(obj-web)/%.html: %.html.in + @mkdir -p $(dir $@) + python ../web/texpand.py $< $@ + +%.gz: % + gzip -9 -c $< > $@ diff -r 5981a0f7540a -r 019040fbf5f5 ja/Makefile.orig --- a/ja/Makefile.orig Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/Makefile.orig Tue Apr 21 00:36:40 2009 +0900 @@ -55,6 +55,8 @@ image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) +#image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png) +image-eps := $(image-dot:%.dot=%.eps) $(image-svg:%.svg=%.eps) $(image-png:%.png=%.eps) example-sources := \ backout \ @@ -108,22 +110,42 @@ hg-version = $(shell hg version -q | \ sed 's,.*(version \(unknown\|[a-f0-9+]*\)),\1,') -all: pdf html +all: dvi + +#dvi: $(sources) $(image-eps) examples +dvi: $(sources) $(image-eps) + platex 00book.tex + + cp 00book.aux hgbook.aux + bibtex hgbook + + platex 00book.tex + platex 00book.tex + platex 00book.tex + + + + + 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 +#pdf/hgbook.pdf: $(sources) $(image-pdf) examples +pdf/hgbook.pdf: $(sources) $(image-pdf) $(call pdf) html: onepage split @@ -153,10 +175,12 @@ rm $(dir $(1))/hgbook.css endef -html/onepage/hgbook.html: $(sources) $(image-html) examples bookhtml.cfg +#html/onepage/hgbook.html: $(sources) $(image-html) examples bookhtml.cfg +html/onepage/hgbook.html: $(sources) $(image-html) bookhtml.cfg $(call htlatex,$@,$<) -html/split/hgbook.html: $(sources) examples bookhtml.cfg +#html/split/hgbook.html: $(sources) examples bookhtml.cfg +html/split/hgbook.html: $(sources) bookhtml.cfg $(call htlatex,$@,$<,2) # Produce 90dpi PNGs for the web. @@ -178,6 +202,9 @@ %.eps: %.dot dot -Tps -o $@ $< +%.eps: %.png + convert $< ps:$@ + examples: $(example-prereqs) examples/.run examples/.run: $(example-sources:%=examples/%.run) @@ -212,5 +239,3 @@ 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 5981a0f7540a -r 019040fbf5f5 ja/examples/bisect --- a/ja/examples/bisect Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/bisect Tue Apr 21 00:36:40 2009 +0900 @@ -1,7 +1,11 @@ #!/bin/bash -#echo '[extensions]' >> $HGRC -#echo 'hbisect =' >> $HGRC +if hg -v | head -1 | grep -e "version 0.*" +then +#On mercurial 1.0 and later bisect is a builtin +echo '[extensions]' >> $HGRC +echo 'hbisect =' >> $HGRC +fi # XXX There's some kind of horrible nondeterminism in the execution of # bisect at the moment. Ugh. @@ -33,15 +37,15 @@ #$ name: search.init -hg bisect --reset +hg bisect init #$ name: search.bad-init -hg bisect --bad +hg bisect bad #$ name: search.good-init -hg bisect --good 10 +hg bisect good 10 #$ name: search.step1 @@ -66,7 +70,7 @@ fi echo this revision is $result - hg bisect --$result + hg bisect $result } #$ name: search.step2 @@ -81,7 +85,7 @@ #$ name: search.reset -hg bisect --reset +hg bisect reset #$ name: diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/branch-named --- a/ja/examples/branch-named Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/branch-named Tue Apr 21 00:36:40 2009 +0900 @@ -64,11 +64,10 @@ #$ name: update-bar hg update bar -hg update -C bar #$ name: merge hg branch -hg merge +hg merge foo hg commit -m 'Merge' hg tip diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/daily.files --- a/ja/examples/daily.files Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/daily.files Tue Apr 21 00:36:40 2009 +0900 @@ -4,9 +4,9 @@ hg init add-example cd add-example -echo a > a +echo a > myfile.txt hg status -hg add a +hg add myfile.txt hg status hg commit -m 'Added one file' hg status @@ -14,10 +14,10 @@ #$ name: add-dir mkdir b -echo b > b/b -echo c > b/c +echo b > b/somefile.txt +echo c > b/source.cpp mkdir b/d -echo d > b/d/d +echo d > b/d/test.h hg add b hg commit -m 'Added all files in subdirectory' diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/rename.divergent --- a/ja/examples/rename.divergent Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/rename.divergent Tue Apr 21 00:36:40 2009 +0900 @@ -14,7 +14,7 @@ #$ name: rename.anne cd anne -hg mv foo bar +hg rename foo bar hg ci -m 'Rename foo to bar' #$ name: rename.bob diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/run-example --- a/ja/examples/run-example Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/run-example Tue Apr 21 00:36:40 2009 +0900 @@ -7,6 +7,7 @@ import cStringIO import errno import getopt +import glob import os import pty import re @@ -18,23 +19,23 @@ import tempfile import time -tex_subs = { - '\\': '\\textbackslash{}', - '{': '\\{', - '}': '\\}', +xml_subs = { + '<': '<', + '>': '>', + '&': '&', } def gensubs(s): start = 0 for i, c in enumerate(s): - sub = tex_subs.get(c) + sub = xml_subs.get(c) if sub: yield s[start:i] start = i + 1 yield sub yield s[start:] -def tex_escape(s): +def xml_escape(s): return ''.join(gensubs(s)) def maybe_unlink(name): @@ -53,7 +54,92 @@ return p return None +def result_name(name): + return os.path.normpath(os.path.join('results', name.replace(os.sep, '-'))) + +def wopen(name): + path = os.path.dirname(name) + if path: + try: + os.makedirs(path) + except OSError, err: + if err.errno != errno.EEXIST: + raise + return open(name, 'w') + class example: + entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml')) + + def __init__(self, name, verbose, keep_change): + self.name = os.path.normpath(name) + self.verbose = verbose + self.keep_change = keep_change + + def status(self, s): + sys.stdout.write(s) + if not s.endswith('\n'): + sys.stdout.flush() + + 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 = result_name(base + '.out') + tmpname = result_name(base + '.tmp') + errname = result_name(base + '.err') + errfp = open(errname, 'w+') + for line in open(tmpname): + errfp.write(mangle_re.sub('', line)) + os.rename(tmpname, result_name(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 + if self.keep_change: + os.rename(errname, oldname) + return False + else: + os.system('diff -u %s %s 1>&2' % (oldname, errname)) + return True + +class static_example(example): + def run(self): + self.status('running %s\n' % self.name) + s = open(self.name).read().rstrip() + s = s.replace('&', '&').replace('<', '<').replace('>', '>') + ofp = wopen(result_name(self.name + '.tmp')) + ofp.write('\n' % self.name) + ofp.write('') + ofp.write(s) + ofp.write('\n') + ofp.write('\n' % self.name) + ofp.close() + self.rename_output(self.name) + norm = self.name.replace(os.sep, '-') + example.entities[ + '' % (norm, norm)] = 1 + + +class shell_example(example): shell = '/usr/bin/env bash' ps1 = '__run_example_ps1__ ' ps2 = '__run_example_ps2__ ' @@ -61,9 +147,8 @@ timeout = 10 - def __init__(self, name, verbose): - self.name = name - self.verbose = verbose + def __init__(self, name, verbose, keep_change): + example.__init__(self, name, verbose, keep_change) self.poll = select.poll() def parse(self): @@ -76,11 +161,6 @@ 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: @@ -146,12 +226,12 @@ maybe_unlink(self.name + '.run') rcfile = os.path.join(tmpdir, '.hgrc') - rcfp = open(rcfile, 'w') + rcfp = wopen(rcfile) print >> rcfp, '[ui]' print >> rcfp, "username = Bryan O'Sullivan " rcfile = os.path.join(tmpdir, '.bashrc') - rcfp = open(rcfile, 'w') + rcfp = wopen(rcfile) print >> rcfp, 'PS1="%s"' % self.ps1 print >> rcfp, 'PS2="%s"' % self.ps2 print >> rcfp, 'unset HISTFILE' @@ -230,12 +310,22 @@ return 1 assert os.sep not in out if ofp is not None: + ofp.write('\n') + ofp.write('\n' % ofp_basename) ofp.close() err |= self.rename_output(ofp_basename, ignore) if out: ofp_basename = '%s.%s' % (self.name, out) + norm = os.path.normpath(ofp_basename) + norm = norm.replace(os.sep, '-') + example.entities[ + '' + % (norm, norm)] = 1 read_hint = ofp_basename + ' ' - ofp = open(ofp_basename + '.tmp', 'w') + ofp = wopen(result_name(ofp_basename + '.tmp')) + ofp.write('\n' % ofp_basename) + ofp.write('') else: ofp = None elif pi == 'ignore': @@ -248,14 +338,15 @@ # first, print the command we ran if not hunk.startswith('#'): nl = hunk.endswith('\n') - hunk = ('%s \\textbf{%s}' % + hunk = ('%s ' + '%s' % (prompts[ps], - tex_escape(hunk.rstrip('\n')))) + xml_escape(hunk.rstrip('\n')))) if nl: hunk += '\n' ofp.write(hunk) # then its output - ofp.write(tex_escape(output)) - ps = newps + ofp.write(xml_escape(output)) + ps = newps self.status('\n') except: print >> sys.stderr, '(killed)' @@ -267,6 +358,8 @@ ps, output = self.sendreceive('exit\n', read_hint) if ofp is not None: ofp.write(output) + ofp.write('\n') + ofp.write('\n' % ofp_basename) ofp.close() err |= self.rename_output(ofp_basename, ignore) os.close(self.cfd) @@ -281,69 +374,40 @@ elif os.WIFSIGNALED(rc): print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc) else: - open(self.name + '.run', 'w') -# return err - return 0 + wopen(result_name(self.name + '.run')) + 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, ' -a --all run all examples in this directory' print >> sys.stderr, ' -h --help print this help message' + print >> sys.stderr, ' --keep keep new output as desired output' print >> sys.stderr, ' -v --verbose display extra debug output' sys.exit(exit) def main(path='.'): + if os.path.realpath(path).split(os.sep)[-1] != 'examples': + print >> sys.stderr, 'Not being run from the examples directory!' + sys.exit(1) + opts, args = getopt.getopt(sys.argv[1:], '?ahv', - ['all', 'help', 'verbose']) + ['all', 'help', 'keep', 'verbose']) verbose = False run_all = False + keep_change = 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 ('--keep',): + keep_change = True if o in ('-v', '--verbose'): verbose = True errs = 0 @@ -355,19 +419,20 @@ 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 + if stat.S_ISREG(st.st_mode): + if st.st_mode & 0111: + if shell_example(a, verbose, keep_change).run(): + errs += 1 + elif a.endswith('.lst'): + static_example(a, verbose, keep_change).run() else: print >> sys.stderr, '%s: not a file, or not executable' % a errs += 1 elif run_all: - names = os.listdir(path) + names = glob.glob("*") + glob.glob("app*/*") + glob.glob("ch*/*") 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 + if name == 'run-example' or name.endswith('~'): continue pathname = os.path.join(path, name) try: st = os.lstat(pathname) @@ -376,12 +441,20 @@ 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() + if stat.S_ISREG(st.st_mode): + if st.st_mode & 0111: + if shell_example(pathname, verbose, keep_change).run(): + errs += 1 + elif pathname.endswith('.lst'): + static_example(pathname, verbose, keep_change).run() + print >> wopen(os.path.join(path, '.run')), time.asctime() else: print_help(1, msg='no test names given, and --all not provided') + + fp = wopen('auto-snippets.xml') + for key in sorted(example.entities.iterkeys()): + print >> fp, key + fp.close() return errs if __name__ == '__main__': diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/template.svnstyle --- a/ja/examples/template.svnstyle Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/template.svnstyle Tue Apr 21 00:36:40 2009 +0900 @@ -34,6 +34,7 @@ hg log -r0 --template '{node}' #$ name: simplest +#$ ignore: \d+-\d+-\d+ \d+:\d+ \+.* cat svn.style hg log -r1 --style svn.style diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/tour --- a/ja/examples/tour Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/tour Tue Apr 21 00:36:40 2009 +0900 @@ -31,7 +31,7 @@ #$ name: log-r hg log -r 3 -hg log -r ff5d7b70a2a9 +hg log -r 0272e0d5a517 hg log -r 1 -r 4 #$ name: log.range @@ -52,10 +52,17 @@ hg clone hello my-hello cd my-hello -#$ name: sed +#$ name: cat1 +cat hello.c + +#$ name: sed -i '/printf/a\\tprintf("hello again!\\n");' hello.c +#$ name: cat2 +# ... edit edit edit ... +cat hello.c + #$ name: status ls @@ -73,6 +80,10 @@ hg commit +#$ name: merge.dummy1 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-hello + #$ name: tip hg tip -vp @@ -135,14 +146,23 @@ hg push http://hg.serpentine.com/tutorial/hello +#$ name: +cp hello.c ../new-hello.c +sed -i '/printf/i\\tprintf("once more, hello.\\n");' ../new-hello.c + #$ 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 +# The file new-hello.c is lightly edited. +cp ../new-hello.c hello.c hg commit -m 'A new hello for a new day.' +#$ name: merge.dummy2 + +hg log -r 5 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV5.my-new-hello + #$ name: merge.cat cat hello.c @@ -152,6 +172,10 @@ hg pull ../my-hello +#$ name: merge.dummy3 + +hg log -r 6 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV6.my-new-hello + #$ name: merge.heads hg heads @@ -173,6 +197,10 @@ hg commit -m 'Merged changes' +#$ name: merge.dummy4 + +hg log -r 7 | grep changeset | cut -c 16-19 2>/dev/null > /tmp/REV7.my-new-hello + #$ name: merge.tip hg tip diff -r 5981a0f7540a -r 019040fbf5f5 ja/examples/tour-merge-conflict --- a/ja/examples/tour-merge-conflict Mon Apr 20 23:50:34 2009 +0900 +++ b/ja/examples/tour-merge-conflict Tue Apr 21 00:36:40 2009 +0900 @@ -68,5 +68,6 @@ Nigerian dictator Sani Abacha. EOF +hg resolve -m letter.txt hg commit -m 'Send me your money' hg tip diff -r 5981a0f7540a -r 019040fbf5f5 po/zh.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/po/zh.po Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,16376 @@ +# +# Simplified Chinese translation for hgbook +# This file is distributed under the same license as the hgbook. +# +# Authors: +# Dongsheng Song , 2009 +# +# Check translation: +# msgfmt --statistics -c -o zh.mo zh.po +# +# Please format your translation before commit: +# msgcat --sort-by-file --width=80 -o zh_new.po zh.po +# mv -f zh_new.po zh.po +# +# Dictionary: +# blame 追溯 +# branch 分支 +# changes 修改 +# changeset 修改集 +# checkout 检出 +# remove 移除(从版本库删除) +# delete 删除(只从文件系统删除) +# filelog 文件日志 +# patchset 补丁集 +# pushing to 推到 +# pulling from 拉自,抓取 +# rename 改名 +# repository 版本库 +# revert 恢复 +# revision 版本 +# revlog 版本日志 +# tag 标签 +# tip 顶点 +# undo 撤销 +# unversioned 未版本控制 +# updated 更新到,同步到(适用于旧版本) +# versioned 受版本控制 +# working copy 工作副本 +# ... +# +msgid "" +msgstr "" +"Project-Id-Version: hgbook 1.2\n" +"POT-Creation-Date: 2009-04-05 11:48+0800\n" +"PO-Revision-Date: 2009-04-05 12:10+0800\n" +"Last-Translator: 宋冬生 \n" +"Language-Team: Simplified Chinese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Chinese\n" +"X-Poedit-Country: CHINA\n" +"X-Poedit-SourceCharset: utf-8\n" + +#. type: Content of: +#: ../en/00book.xml:41 +msgid "Mercurial: The Definitive Guide" +msgstr "Mercurial 权威指南" + +#. type: Content of: <book><subtitle> +#: ../en/00book.xml:46 +msgid "Compiled from $rev_id$" +msgstr "编译自 $rev_id$" + +#. type: Content of: <book><bookinfo> +#: ../en/00book.xml:48 +msgid "<edition>1</edition> <isbn>9780596800673</isbn>" +msgstr "<edition>1</edition> <isbn>9780596800673</isbn>" + +#. type: Content of: <book><bookinfo><authorgroup><author><firstname> +#: ../en/00book.xml:52 +msgid "Bryan" +msgstr "Bryan" + +#. type: Content of: <book><bookinfo><authorgroup><author><surname> +#: ../en/00book.xml:53 +msgid "O'Sullivan" +msgstr "O'Sullivan" + +#. type: Content of: <book><bookinfo> +#: ../en/00book.xml:57 +msgid "" +"<editor> <firstname>Mike</firstname> <surname>Loukides</surname> </editor> " +"<copyright> <year>2006</year> <year>2007</year> <year>2008</year> <year>2009</" +"year> <holder>Bryan O'Sullivan</holder> </copyright>" +msgstr "" +"<editor> <firstname>Mike</firstname> <surname>Loukides</surname> </editor> " +"<copyright> <year>2006</year> <year>2007</year> <year>2008</year> <year>2009</" +"year> <holder>Bryan O'Sullivan</holder> </copyright>" + +#. type: Content of: <book><appendix><title> +#: ../en/appB-mq-ref.xml:5 +msgid "Mercurial Queues reference" +msgstr "Mercurial 队列参考" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appB-mq-ref.xml:8 +msgid "MQ command reference" +msgstr "MQ 命令参考" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appB-mq-ref.xml:10 +msgid "" +"For an overview of the commands provided by MQ, use the command <command role=" +"\"hg-cmd\">hg help mq</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:14 +msgid "" +"<command role=\"hg-ext-mq\">qapplied</command>&emdash;print applied patches" +msgstr "<command role=\"hg-ext-mq\">qapplied</command>—显示已应用的补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:17 +msgid "" +"The <command role=\"hg-ext-mq\">qapplied</command> 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 <quote>top</quote> patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:24 +msgid "" +"<command role=\"hg-ext-mq\">qcommit</command>&emdash;commit changes in the " +"queue repository" +msgstr "<command role=\"hg-ext-mq\">qcommit</command>—提交队列中的修改" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:27 +msgid "" +"The <command role=\"hg-ext-mq\">qcommit</command> command commits any " +"outstanding changes in the <filename role=\"special\" class=\"directory\">.hg/" +"patches</filename> repository. This command only works if the <filename role=" +"\"special\" class=\"directory\">.hg/patches</filename> directory is a " +"repository, i.e. you created the directory using <command role=\"hg-cmd\">hg " +"qinit <option role=\"hg-ext-mq-cmd-qinit-opt\">-c</option></command> or ran " +"<command role=\"hg-cmd\">hg init</command> in the directory after running " +"<command role=\"hg-ext-mq\">qinit</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:39 +msgid "" +"This command is shorthand for <command role=\"hg-cmd\">hg commit --cwd .hg/" +"patches</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:43 +msgid "" +"<command role=\"hg-ext-mq\">qdelete</command>&emdash;delete a patch from the " +"<filename role=\"special\">series</filename> file" +msgstr "" +"<command role=\"hg-ext-mq\">qdelete</command>—从文件 <filename role=\"special" +"\">series</filename> 中删除补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:48 +msgid "" +"The <command role=\"hg-ext-mq\">qdelete</command> command removes the entry " +"for a patch from the <filename role=\"special\">series</filename> file in the " +"<filename role=\"special\" class=\"directory\">.hg/patches</filename> " +"directory. It does not pop the patch if the patch is already applied. By " +"default, it does not delete the patch file; use the <option role=\"hg-ext-mq-" +"cmd-qdel-opt\">-f</option> option to do that." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:57 ../en/appB-mq-ref.xml:99 ../en/appB-mq-ref.xml:157 +#: ../en/appB-mq-ref.xml:197 ../en/appB-mq-ref.xml:264 +#: ../en/appB-mq-ref.xml:335 ../en/appB-mq-ref.xml:404 +#: ../en/appB-mq-ref.xml:497 +msgid "Options:" +msgstr "选项:" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:59 +msgid "" +"<option role=\"hg-ext-mq-cmd-qdel-opt\">-f</option>: Delete the patch file." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:66 +msgid "" +"<command role=\"hg-ext-mq\">qdiff</command>&emdash;print a diff of the " +"topmost applied patch" +msgstr "<command role=\"hg-ext-mq\">qdiff</command>—显示最新应用补丁的差异" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:69 +msgid "" +"The <command role=\"hg-ext-mq\">qdiff</command> command prints a diff of the " +"topmost applied patch. It is equivalent to <command role=\"hg-cmd\">hg diff -" +"r-2:-1</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:75 +msgid "" +"<command role=\"hg-ext-mq\">qfold</command>&emdash;merge (<quote>fold</" +"quote>) several patches into one" +msgstr "" +"<command role=\"hg-ext-mq\">qfold</command>—将多个补丁合并(<quote>折叠</" +"quote>)成一个" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:78 +msgid "" +"The <command role=\"hg-ext-mq\">qfold</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:83 +msgid "" +"The patches to fold must not be applied; <command role=\"hg-ext-mq\">qfold</" +"command> will exit with an error if any is. The order in which patches are " +"folded is significant; <command role=\"hg-cmd\">hg qfold a b</command> means " +"<quote>apply the current topmost patch, followed by <literal>a</literal>, " +"followed by <literal>b</literal></quote>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:91 +msgid "" +"The comments from the folded patches are appended to the comments of the " +"destination patch, with each block of comments separated by three asterisk " +"(<quote><literal>*</literal></quote>) characters. Use the <option role=\"hg-" +"ext-mq-cmd-qfold-opt\">-e</option> option to edit the commit message for the " +"combined patch/changeset after the folding has completed." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:101 +msgid "" +"<option role=\"hg-ext-mq-cmd-qfold-opt\">-e</option>: Edit the commit message " +"and patch description for the newly folded patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:106 +msgid "" +"<option role=\"hg-ext-mq-cmd-qfold-opt\">-l</option>: Use the contents of the " +"given file as the new commit message and patch description for the folded " +"patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:111 +msgid "" +"<option role=\"hg-ext-mq-cmd-qfold-opt\">-m</option>: Use the given text as " +"the new commit message and patch description for the folded patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:119 +msgid "" +"<command role=\"hg-ext-mq\">qheader</command>&emdash;display the header/" +"description of a patch" +msgstr "<command role=\"hg-ext-mq\">qheader</command>—显示补丁头部描述" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:123 +msgid "" +"The <command role=\"hg-ext-mq\">qheader</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:130 +msgid "" +"<command role=\"hg-ext-mq\">qimport</command>&emdash;import a third-party " +"patch into the queue" +msgstr "<command role=\"hg-ext-mq\">qimport</command>—将第三方补丁导入队列" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:133 +msgid "" +"The <command role=\"hg-ext-mq\">qimport</command> command adds an entry for " +"an external patch to the <filename role=\"special\">series</filename> file, " +"and copies the patch into the <filename role=\"special\" class=\"directory\">." +"hg/patches</filename> directory. It adds the entry immediately after the " +"topmost applied patch, but does not push the patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:141 +msgid "" +"If the <filename role=\"special\" class=\"directory\">.hg/patches</filename> " +"directory is a repository, <command role=\"hg-ext-mq\">qimport</command> " +"automatically does an <command role=\"hg-cmd\">hg add</command> of the " +"imported patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:149 +msgid "" +"<command role=\"hg-ext-mq\">qinit</command>&emdash;prepare a repository to " +"work with MQ" +msgstr "<command role=\"hg-ext-mq\">qinit</command>—为使用 MQ 配置版本库" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:152 +msgid "" +"The <command role=\"hg-ext-mq\">qinit</command> command prepares a repository " +"to work with MQ. It creates a directory called <filename role=\"special\" " +"class=\"directory\">.hg/patches</filename>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:159 +msgid "" +"<option role=\"hg-ext-mq-cmd-qinit-opt\">-c</option>: Create <filename role=" +"\"special\" class=\"directory\">.hg/patches</filename> as a repository in its " +"own right. Also creates a <filename role=\"special\">.hgignore</filename> " +"file that will ignore the <filename role=\"special\">status</filename> file." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:169 +msgid "" +"When the <filename role=\"special\" class=\"directory\">.hg/patches</" +"filename> directory is a repository, the <command role=\"hg-ext-mq\">qimport</" +"command> and <command role=\"hg-ext-mq\">qnew</command> commands " +"automatically <command role=\"hg-cmd\">hg add</command> new patches." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:178 +msgid "<command role=\"hg-ext-mq\">qnew</command>&emdash;create a new patch" +msgstr "<command role=\"hg-ext-mq\">qnew</command>—创建新补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:181 +msgid "" +"The <command role=\"hg-ext-mq\">qnew</command> 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 " +"<filename role=\"special\">series</filename> file after the current topmost " +"applied patch, and is immediately pushed on top of that patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:189 +msgid "" +"If <command role=\"hg-ext-mq\">qnew</command> finds modified files in the " +"working directory, it will refuse to create a new patch unless the <option " +"role=\"hg-ext-mq-cmd-qnew-opt\">-f</option> option is used (see below). This " +"behaviour allows you to <command role=\"hg-ext-mq\">qrefresh</command> your " +"topmost applied patch before you apply a new patch on top of it." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:199 +msgid "" +"<option role=\"hg-ext-mq-cmd-qnew-opt\">-f</option>: 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:206 +msgid "" +"<option role=\"hg-ext-mq-cmd-qnew-opt\">-m</option>: Use the given text as " +"the commit message. This text will be stored at the beginning of the patch " +"file, before the patch data." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:215 +msgid "" +"<command role=\"hg-ext-mq\">qnext</command>&emdash;print the name of the next " +"patch" +msgstr "<command role=\"hg-ext-mq\">qnext</command>—显示下个补丁的名称" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:218 +msgid "" +"The <command role=\"hg-ext-mq\">qnext</command> command prints the name name " +"of the next patch in the <filename role=\"special\">series</filename> file " +"after the topmost applied patch. This patch will become the topmost applied " +"patch if you run <command role=\"hg-ext-mq\">qpush</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:227 +msgid "" +"<command role=\"hg-ext-mq\">qpop</command>&emdash;pop patches off the stack" +msgstr "<command role=\"hg-ext-mq\">qpop</command>—删除堆栈顶部的补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:230 +msgid "" +"The <command role=\"hg-ext-mq\">qpop</command> command removes applied " +"patches from the top of the stack of applied patches. By default, it removes " +"only one patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:234 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:238 +msgid "" +"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, <command role=\"hg-" +"ext-mq\">qpop</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:248 +msgid "" +"The <command role=\"hg-ext-mq\">qpop</command> command does not read or write " +"patches or the <filename role=\"special\">series</filename> file. It is thus " +"safe to <command role=\"hg-ext-mq\">qpop</command> a patch that you have " +"removed from the <filename role=\"special\">series</filename> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:257 +msgid "" +"By default, the <command role=\"hg-ext-mq\">qpop</command> command will not " +"pop any patches if the working directory has been modified. You can override " +"this behaviour using the <option role=\"hg-ext-mq-cmd-qpop-opt\">-f</option> " +"option, which reverts all modifications in the working directory." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:266 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpop-opt\">-a</option>: Pop all applied " +"patches. This returns the repository to its state before you applied any " +"patches." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:271 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpop-opt\">-f</option>: Forcibly revert any " +"modifications to the working directory when popping." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:276 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpop-opt\">-n</option>: Pop a patch from the " +"named queue." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:281 +msgid "" +"The <command role=\"hg-ext-mq\">qpop</command> command removes one line from " +"the end of the <filename role=\"special\">status</filename> file for each " +"patch that it pops." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:288 +msgid "" +"<command role=\"hg-ext-mq\">qprev</command>&emdash;print the name of the " +"previous patch" +msgstr "<command role=\"hg-ext-mq\">qprev</command>—显示上个补丁的名称" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:291 +msgid "" +"The <command role=\"hg-ext-mq\">qprev</command> command prints the name of " +"the patch in the <filename role=\"special\">series</filename> file that comes " +"before the topmost applied patch. This will become the topmost applied patch " +"if you run <command role=\"hg-ext-mq\">qpop</command>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:300 +msgid "" +"<command role=\"hg-ext-mq\">qpush</command>&emdash;push patches onto the stack" +msgstr "<command role=\"hg-ext-mq\">qpush</command>—增加补丁到堆栈" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:303 +msgid "" +"The <command role=\"hg-ext-mq\">qpush</command> command adds patches onto the " +"applied stack. By default, it adds only one patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:307 +msgid "" +"This command creates a new changeset to represent each applied patch, and " +"updates the working directory to apply the effects of the patches." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:311 +msgid "The default data used when creating a changeset are as follows:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:314 +msgid "" +"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 <command role=\"hg-ext-mq\">qpop</command> a patch and <command role=" +"\"hg-ext-mq\">qpush</command> it again, the changeset that you push will have " +"a different identity than the changeset you popped." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:322 +msgid "" +"The author is the same as the default used by the <command role=\"hg-cmd\">hg " +"commit</command> command." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:326 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:331 +msgid "" +"If a patch contains a Mercurial patch header (XXX add link), the information " +"in the patch header overrides these defaults." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:337 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpush-opt\">-a</option>: Push all unapplied " +"patches from the <filename role=\"special\">series</filename> file until " +"there are none left to push." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:343 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpush-opt\">-l</option>: Add the name of the " +"patch to the end of the commit message." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:347 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpush-opt\">-m</option>: 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:355 +msgid "" +"<option role=\"hg-ext-mq-cmd-qpush-opt\">-n</option>: Use the named queue if " +"merging while pushing." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:360 +msgid "" +"The <command role=\"hg-ext-mq\">qpush</command> command reads, but does not " +"modify, the <filename role=\"special\">series</filename> file. It appends " +"one line to the <command role=\"hg-cmd\">hg status</command> file for each " +"patch that it pushes." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:368 +msgid "" +"<command role=\"hg-ext-mq\">qrefresh</command>&emdash;update the topmost " +"applied patch" +msgstr "<command role=\"hg-ext-mq\">qrefresh</command>—更新最新的补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:372 +msgid "" +"The <command role=\"hg-ext-mq\">qrefresh</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:378 +msgid "" +"The <command role=\"hg-ext-mq\">qrefresh</command> command looks for the " +"following modifications:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:381 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:386 +msgid "" +"Modifications to tracked files in the working directory are added to the " +"patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:389 +msgid "" +"Changes to the files tracked using <command role=\"hg-cmd\">hg add</command>, " +"<command role=\"hg-cmd\">hg copy</command>, <command role=\"hg-cmd\">hg " +"remove</command>, or <command role=\"hg-cmd\">hg rename</command>. Added " +"files and copy and rename destinations are added to the patch, while removed " +"files and rename sources are removed." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:398 +msgid "" +"Even if <command role=\"hg-ext-mq\">qrefresh</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:406 +msgid "" +"<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-e</option>: Modify the commit " +"and patch description, using the preferred text editor." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:411 +msgid "" +"<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-m</option>: Modify the commit " +"message and patch description, using the given text." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:416 +msgid "" +"<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-l</option>: Modify the commit " +"message and patch description, using text from the given file." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:424 +msgid "<command role=\"hg-ext-mq\">qrename</command>&emdash;rename a patch" +msgstr "<command role=\"hg-ext-mq\">qrename</command>—改名补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:427 +msgid "" +"The <command role=\"hg-ext-mq\">qrename</command> command renames a patch, " +"and changes the entry for the patch in the <filename role=\"special\">series</" +"filename> file." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:431 +msgid "" +"With a single argument, <command role=\"hg-ext-mq\">qrename</command> renames " +"the topmost applied patch. With two arguments, it renames its first argument " +"to its second." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:438 +msgid "" +"<command role=\"hg-ext-mq\">qrestore</command>&emdash;restore saved queue " +"state" +msgstr "<command role=\"hg-ext-mq\">qrestore</command>—恢复保存的队列" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:442 +msgid "XXX No idea what this does." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:446 +msgid "" +"<command role=\"hg-ext-mq\">qsave</command>&emdash;save current queue state" +msgstr "<command role=\"hg-ext-mq\">qsave</command>—保存当前的队列状态" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:449 +msgid "XXX Likewise." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:453 +msgid "" +"<command role=\"hg-ext-mq\">qseries</command>&emdash;print the entire patch " +"series" +msgstr "<command role=\"hg-ext-mq\">qseries</command>—显示补丁序列" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:456 +msgid "" +"The <command role=\"hg-ext-mq\">qseries</command> command prints the entire " +"patch series from the <filename role=\"special\">series</filename> file. It " +"prints only patch names, not empty lines or comments. It prints in order " +"from first to be applied to last." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:464 +msgid "" +"<command role=\"hg-ext-mq\">qtop</command>&emdash;print the name of the " +"current patch" +msgstr "<command role=\"hg-ext-mq\">qtop</command>—显示当前补丁的名称" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:467 +msgid "" +"The <command role=\"hg-ext-mq\">qtop</command> prints the name of the topmost " +"currently applied patch." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:472 +msgid "" +"<command role=\"hg-ext-mq\">qunapplied</command>&emdash;print patches not yet " +"applied" +msgstr "<command role=\"hg-ext-mq\">qunapplied</command>—显示尚未应用的补丁" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:476 +msgid "" +"The <command role=\"hg-ext-mq\">qunapplied</command> command prints the names " +"of patches from the <filename role=\"special\">series</filename> file that " +"are not yet applied. It prints them in order from the next patch that will " +"be pushed to the last." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:484 +msgid "" +"<command role=\"hg-cmd\">hg strip</command>&emdash;remove a revision and " +"descendants" +msgstr "<command role=\"hg-cmd\">hg strip</command>—删除一个版本及其后继" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:487 +msgid "" +"The <command role=\"hg-cmd\">hg strip</command> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:493 +msgid "" +"The <command role=\"hg-cmd\">hg strip</command> command saves a backup of the " +"removed changesets in a bundle, so that they can be reapplied if removed in " +"error." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:499 +msgid "" +"<option role=\"hg-opt-strip\">-b</option>: Save unrelated changesets that are " +"intermixed with the stripped changesets in the backup bundle." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:503 +msgid "" +"<option role=\"hg-opt-strip\">-f</option>: If a branch has multiple heads, " +"remove all heads. XXX This should be renamed, and use <literal>-f</literal> " +"to strip revs when there are pending changes." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> +#: ../en/appB-mq-ref.xml:508 +msgid "<option role=\"hg-opt-strip\">-n</option>: Do not save a backup bundle." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appB-mq-ref.xml:515 +msgid "MQ file reference" +msgstr "MQ 文件参考" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:518 +msgid "The <filename role=\"special\">series</filename> file" +msgstr "<filename role=\"special\">序列</filename>文件" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:521 +msgid "" +"The <filename role=\"special\">series</filename> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:527 +msgid "" +"Lines may contain comments. A comment begins with the <quote><literal>#</" +"literal></quote> character, and extends to the end of the line. Empty lines, " +"and lines that contain only comments, are ignored." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:532 +msgid "" +"You will often need to edit the <filename role=\"special\">series</filename> " +"file by hand, hence the support for comments and empty lines noted above. " +"For example, you can comment out a patch temporarily, and <command role=\"hg-" +"ext-mq\">qpush</command> 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 <filename role=\"special\">series</filename> file." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:541 +msgid "" +"Placing the <filename role=\"special\">series</filename> file under revision " +"control is also supported; it is a good idea to place all of the patches that " +"it refers to under revision control, as well. If you create a patch " +"directory using the <option role=\"hg-ext-mq-cmd-qinit-opt\">-c</option> " +"option to <command role=\"hg-ext-mq\">qinit</command>, this will be done for " +"you automatically." +msgstr "" + +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:551 +msgid "The <filename role=\"special\">status</filename> file" +msgstr "<filename role=\"special\">状态</filename>文件" + +#. type: Content of: <book><appendix><sect1><sect2><para> +#: ../en/appB-mq-ref.xml:554 +msgid "" +"The <filename role=\"special\">status</filename> file contains the names and " +"changeset hashes of all patches that MQ currently has applied. Unlike the " +"<filename role=\"special\">series</filename> 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." +msgstr "" + +#. type: Content of: <book><appendix><title> +#: ../en/appC-srcinstall.xml:5 +msgid "Installing Mercurial from source" +msgstr "从源代码安装 Mercurial" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appC-srcinstall.xml:8 +msgid "On a Unix-like system" +msgstr "类 Unix 系统" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appC-srcinstall.xml:10 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appC-srcinstall.xml:14 +msgid "" +"Download a recent source tarball from <ulink url=\"http://www.selenic.com/" +"mercurial/download\">http://www.selenic.com/mercurial/download</ulink>." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appC-srcinstall.xml:17 +msgid "Unpack the tarball:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appC-srcinstall.xml:20 +msgid "" +"Go into the source directory and run the installer script. This will build " +"Mercurial and install it in your home directory." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appC-srcinstall.xml:27 +msgid "" +"Once the install finishes, Mercurial will be in the <literal>bin</literal> " +"subdirectory of your home directory. Don't forget to make sure that this " +"directory is present in your shell's search path." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appC-srcinstall.xml:32 +msgid "" +"You will probably need to set the <envar>PYTHONPATH</envar> 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 <literal>/home/bos/lib/" +"python</literal>. 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 <literal>mercurial</literal> directory were " +"installed to." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appC-srcinstall.xml:44 +msgid "On Windows" +msgstr "Windows 系统" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appC-srcinstall.xml:46 +msgid "" +"Building and installing Mercurial on Windows requires a variety of tools, a " +"fair amount of technical knowledge, and considerable patience. I very much " +"<emphasis>do not recommend</emphasis> this route if you are a <quote>casual " +"user</quote>. Unless you intend to hack on Mercurial, I strongly suggest " +"that you use a binary package instead." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appC-srcinstall.xml:53 +msgid "" +"If you are intent on building Mercurial from source on Windows, follow the " +"<quote>hard way</quote> directions on the Mercurial wiki at <ulink url=" +"\"http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall\">http://www." +"selenic.com/mercurial/wiki/index.cgi/WindowsInstall</ulink>, and expect the " +"process to involve a lot of fiddly work." +msgstr "" + +#. type: Content of: <book><appendix><title> +#: ../en/appD-license.xml:5 +msgid "Open Publication License" +msgstr "" + +#. type: Content of: <book><appendix><para> +#: ../en/appD-license.xml:7 +msgid "Version 1.0, 8 June 1999" +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:10 +msgid "Requirements on both unmodified and modified versions" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:13 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:20 +msgid "Proper form for an incorporation by reference is as follows:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><blockquote><para> +#: ../en/appD-license.xml:24 +msgid "" +"Copyright (c) <emphasis>year</emphasis> by <emphasis>author's name or " +"designee</emphasis>. This material may be distributed only subject to the " +"terms and conditions set forth in the Open Publication License, v<emphasis>x." +"y</emphasis> or later (the latest version is presently available at <ulink " +"url=\"http://www.opencontent.org/openpub/\">http://www.opencontent.org/" +"openpub/</ulink>)." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:33 +msgid "" +"The reference must be immediately followed with any options elected by the " +"author(s) and/or publisher of the document (see <xref linkend=\"sec:opl:" +"options\"/>)." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:37 +msgid "" +"Commercial redistribution of Open Publication-licensed material is permitted." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:40 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:49 +msgid "Copyright" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:51 +msgid "" +"The copyright to each Open Publication is owned by its author(s) or designee." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:56 +msgid "Scope of license" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:58 +msgid "" +"The following license terms apply to all Open Publication works, unless " +"otherwise explicitly stated in the document." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:62 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:69 +msgid "" +"<emphasis role=\"bold\">Severability</emphasis>. If any part of this license " +"is found to be unenforceable in any jurisdiction, the remaining portions of " +"the license remain in force." +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:74 +msgid "" +"<emphasis role=\"bold\">No warranty</emphasis>. Open Publication works are " +"licensed and provided <quote>as is</quote> 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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:83 +msgid "Requirements on modified works" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:85 +msgid "" +"All modified versions of documents covered by this license, including " +"translations, anthologies, compilations and partial documents, must meet the " +"following requirements:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:90 +msgid "The modified version must be labeled as such." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:93 +msgid "" +"The person making the modifications must be identified and the modifications " +"dated." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:96 +msgid "" +"Acknowledgement of the original author and publisher if applicable must be " +"retained according to normal academic citation practices." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:100 +msgid "The location of the original unmodified document must be identified." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:103 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:111 +msgid "Good-practice recommendations" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:113 +msgid "" +"In addition to the requirements of this license, it is requested from and " +"strongly recommended of redistributors that:" +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:118 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:125 +msgid "" +"All substantive modifications (including deletions) be either clearly marked " +"up in the document or else described in an attachment to the document." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:129 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><appendix><sect1><title> +#: ../en/appD-license.xml:137 +msgid "License options" +msgstr "" + +#. type: Content of: <book><appendix><sect1><para> +#: ../en/appD-license.xml:139 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:147 +msgid "" +"To prohibit distribution of substantively modified versions without the " +"explicit permission of the author(s). <quote>Substantive modification</quote> " +"is defined as a change to the semantic content of the document, and excludes " +"mere changes in format or typographical corrections." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:154 +msgid "" +"To accomplish this, add the phrase <quote>Distribution of substantively " +"modified versions of this document is prohibited without the explicit " +"permission of the copyright holder.</quote> to the license reference or copy." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:160 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><appendix><sect1><orderedlist><listitem><para> +#: ../en/appD-license.xml:165 +msgid "" +"To accomplish this, add the phrase <quote>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.</quote> to the " +"license reference or copy." +msgstr "" + +#. type: Content of: <book><preface><title> +#: ../en/ch00-preface.xml:5 +msgid "Preface" +msgstr "序言" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:8 +msgid "Why revision control? Why Mercurial?" +msgstr "为什么使用版本控制? 为什么使用 Mercurial?" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:10 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:16 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:27 +msgid "" +"The arrival of distributed revision control is relatively recent, and so far " +"this new field has grown due to people's willingness to explore ill-charted " +"territory." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:31 +msgid "" +"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 buckle." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:39 +msgid "Why use revision control?" +msgstr "为什么使用版本控制?" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:41 +msgid "" +"There are a number of reasons why you or your team might want to use an " +"automated revision control tool for a project." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:46 +msgid "" +"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 <emphasis>who</emphasis> made it; " +"<emphasis>why</emphasis> they made it; <emphasis>when</emphasis> they made " +"it; and <emphasis>what</emphasis> the change was." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:53 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:59 +msgid "" +"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 <emphasis>really</emphasis> good revision control tool " +"will even help you to efficiently figure out exactly when a problem was " +"introduced (see <xref linkend=\"sec:undo:bisect\"/> for details)." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:66 +msgid "" +"It will help you to work simultaneously on, and manage the drift between, " +"multiple versions of your project." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:71 +msgid "" +"Most of these reasons are equally valid&emdash;at least in theory&emdash;" +"whether you're working on a project by yourself, or with a hundred other " +"people." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:76 +msgid "" +"A key question about the practicality of revision control at these two " +"different scales (<quote>lone hacker</quote> and <quote>huge team</quote>) is " +"how its <emphasis>benefits</emphasis> compare to its <emphasis>costs</" +"emphasis>. A revision control tool that's difficult to understand or use is " +"going to impose a high cost." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:84 +msgid "" +"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 " +"<emphasis>without</emphasis> it, failure is almost guaranteed." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:91 +msgid "" +"On the other hand, a one-person <quote>quick hack</quote> 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?" +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:96 +msgid "" +"Mercurial uniquely supports <emphasis>both</emphasis> 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 " +"<emphasis>really</emphasis> trying to do. At the same time, Mercurial's high " +"performance and peer-to-peer nature let you scale painlessly to handle large " +"projects." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:106 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:113 +msgid "The many names of revision control" +msgstr "版本控制的别名" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:115 +msgid "" +"Revision control is a diverse field, so much so that it is referred to by " +"many names and acronyms. Here are a few of the more common variations you'll " +"encounter:" +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:119 +msgid "Revision control (RCS)" +msgstr "版本控制(RCS)" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:120 +msgid "Software configuration management (SCM), or configuration management" +msgstr "软件配置管理(SCM),或配置管理" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:122 +msgid "Source code management" +msgstr "源代码管理" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:123 +msgid "Source code control, or source control" +msgstr "源代码控制,或源控制" + +#. type: Content of: <book><preface><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:125 +msgid "Version control (VCS)" +msgstr "版本控制(VCS)" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:127 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:135 +msgid "This book is a work in progress" +msgstr "本书正在编写中" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:137 +msgid "" +"I am releasing this book while I am still writing it, in the hope that it " +"will prove useful to others. I am writing under an open license in the hope " +"that you, my readers, will contribute feedback and perhaps content of your " +"own." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:144 +msgid "About the examples in this book" +msgstr "本书的例子" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:146 +msgid "" +"This book takes an unusual approach to code samples. Every example is " +"<quote>live</quote>&emdash;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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:153 +msgid "" +"The advantage of this approach is that the examples are always accurate; they " +"describe <emphasis>exactly</emphasis> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:160 +msgid "" +"There is a small disadvantage to this approach, which is that the dates and " +"times you'll see in examples tend to be <quote>squashed</quote> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:168 +msgid "" +"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 " +"<literal role=\"hg-ext\">bisect</literal> example in <xref linkend=\"sec:undo:" +"bisect\"/>, for instance." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:174 +msgid "" +"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 <emphasis>do</emphasis> be " +"confident that the behaviour you're seeing is consistent and reproducible." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:182 +msgid "Trends in the field" +msgstr "版本控制的发展趋势" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:184 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:189 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:195 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:207 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:219 +msgid "A few of the advantages of distributed revision control" +msgstr "分布版本控制的优点" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:222 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:229 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:240 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:248 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:259 +msgid "Advantages for open source projects" +msgstr "开源项目的优点" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:261 +msgid "" +"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 " +"<quote>core</quote> 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 <quote>read only</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><title> +#: ../en/ch00-preface.xml:277 +msgid "The forking non-problem" +msgstr "分叉不是问题" + +#. type: Content of: <book><preface><sect1><sect2><sect3><para> +#: ../en/ch00-preface.xml:279 +msgid "" +"It has been suggested that distributed revision control tools pose some sort " +"of risk to open source projects because they make it easy to <quote>fork</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><para> +#: ../en/ch00-preface.xml:289 +msgid "" +"Sometimes the camps in a fork decide to reconcile their differences. With a " +"centralised revision control system, the <emphasis>technical</emphasis> " +"process of reconciliation is painful, and has to be performed largely by " +"hand. You have to decide whose revision history is going to <quote>win</" +"quote>, and graft the other team's changes into the tree somehow. This " +"usually loses some or all of one side's revision history." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><para> +#: ../en/ch00-preface.xml:298 +msgid "" +"What distributed tools do with respect to forking is they make forking the " +"<emphasis>only</emphasis> 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 " +"<emphasis>merging</emphasis> forks, because forks are absolutely fundamental: " +"they happen all the time." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><para> +#: ../en/ch00-preface.xml:307 +msgid "" +"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 " +"<quote>fork</quote> becomes <emphasis>purely</emphasis> a social issue. If " +"anything, distributed tools <emphasis>lower</emphasis> the likelihood of a " +"fork:" +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:314 +msgid "" +"They eliminate the social distinction that centralised tools impose: that " +"between insiders (people with commit access) and outsiders (people without)." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:318 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><sect3><para> +#: ../en/ch00-preface.xml:323 +msgid "" +"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 publicly, 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:338 +msgid "Advantages for commercial projects" +msgstr "商业项目的优点" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:340 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:352 +msgid "" +"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&emdash;if you have one at " +"all&emdash;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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:364 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:375 +msgid "Why choose Mercurial?" +msgstr "为什么选择 Mercurial?" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:377 +msgid "" +"Mercurial has a unique set of properties that make it a particularly good " +"choice as a revision control system." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:380 +msgid "It is easy to learn and use." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:381 +msgid "It is lightweight." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:382 +msgid "It scales excellently." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:383 +msgid "It is easy to customise." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:386 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:393 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:400 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:405 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:415 +msgid "Mercurial compared with other tools" +msgstr "Mercurial 与其它工具的比较" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:417 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:425 ../en/ch00-preface.xml:636 +msgid "Subversion" +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:427 +msgid "" +"Subversion is a popular revision control tool, developed to replace CVS. It " +"has a centralised client/server architecture." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:431 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:436 +msgid "" +"Prior to version 1.5, Subversion had no useful support for merges. At the " +"time of writing, its merge tracking capability is new, and known to be <ulink " +"url=\"http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced." +"html#svn.branchmerge.advanced.finalword\">complicated and buggy</ulink>." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:442 +msgid "" +"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 <emphasis>ra_local</emphasis> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:455 +msgid "" +"Additionally, Subversion incurs substantial storage overhead to avoid network " +"transactions for a few common operations, such as finding modified files " +"(<literal>status</literal>) and displaying modifications against the current " +"revision (<literal>diff</literal>). 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:465 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:471 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:480 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:487 +msgid "" +"Mercurial can import revision history from a Subversion repository. It can " +"also export revision history to a Subversion repository. This makes it easy " +"to <quote>test the waters</quote> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:499 ../en/ch00-preface.xml:638 +msgid "Git" +msgstr "Git" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:501 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:506 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:511 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:518 +msgid "" +"While a Mercurial repository needs no maintenance, a Git repository requires " +"frequent manual <quote>repacks</quote> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:529 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:535 +msgid "Mercurial can import revision history from a Git repository." +msgstr "" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:541 ../en/ch00-preface.xml:637 +msgid "CVS" +msgstr "CVS" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:543 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:547 +msgid "" +"It has a centralised client/server architecture. It does not group related " +"file changes into atomic commits, making it easy for people to <quote>break " +"the build</quote>: 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)." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:559 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:567 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:581 +msgid "Mercurial can import revision history from a CVS repository." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:587 +msgid "Commercial tools" +msgstr "商业工具" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:589 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:595 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><title> +#: ../en/ch00-preface.xml:604 +msgid "Choosing a revision control tool" +msgstr "选择版本控制工具" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:606 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:611 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><sect2><para> +#: ../en/ch00-preface.xml:615 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:623 +msgid "Switching from another tool to Mercurial" +msgstr "从其它工具切换到 Mercurial" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:625 +msgid "" +"Mercurial is bundled with an extension named <literal role=\"hg-ext" +"\">convert</literal>, which can incrementally import revision history from " +"several other revision control tools. By <quote>incremental</quote>, 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:633 +msgid "" +"The revision control tools supported by <literal role=\"hg-ext\">convert</" +"literal> are as follows:" +msgstr "<literal role=\"hg-ext\">convert</literal> 支持的版本控制工具有:" + +#. type: Content of: <book><preface><sect1><itemizedlist><listitem><para> +#: ../en/ch00-preface.xml:639 +msgid "Darcs" +msgstr "Darcs" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:641 +msgid "" +"In addition, <literal role=\"hg-ext\">convert</literal> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:647 +msgid "" +"The <command role=\"hg-ext-convert\">convert</command> 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:656 +msgid "A short history of revision control" +msgstr "版本控制简史" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:658 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:669 +msgid "" +"Walter Tichy developed a free alternative to SCCS in the early 1980s; he " +"called his program RCS (Revision 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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:675 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:686 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:697 +msgid "" +"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.)" +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:704 +msgid "" +"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 <quote>pain level</quote> of fixing these " +"architectural problems prohibitive." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:714 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:723 +msgid "" +"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 <quote>trust</quote> for code from different sources." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:732 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><preface><sect1><title> +#: ../en/ch00-preface.xml:740 +msgid "Colophon&emdash;this book is Free" +msgstr "后记—本书是自由的!" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:742 +msgid "" +"This book is licensed under the Open Publication License, and is produced " +"entirely using Free Software tools. It is typeset with DocBook XML. " +"Illustrations are drawn and rendered with <ulink url=\"http://www.inkscape." +"org/\">Inkscape</ulink>." +msgstr "" + +#. type: Content of: <book><preface><sect1><para> +#: ../en/ch00-preface.xml:747 +msgid "" +"The complete source code for this book is published as a Mercurial " +"repository, at <ulink url=\"http://hg.serpentine.com/mercurial/book\">http://" +"hg.serpentine.com/mercurial/book</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch01-tour-basic.xml:5 +msgid "A tour of Mercurial: the basics" +msgstr "Mercurial 教程: 基础知识" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:8 +msgid "Installing Mercurial on your system" +msgstr "安装 Mercurial" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:10 +msgid "" +"Prebuilt binary packages of Mercurial are available for every popular " +"operating system. These make it easy to start using Mercurial on your " +"computer immediately." +msgstr "" +"对于每种流行的操作系统,都有已经构建的二进制软件包。这让在你的计算机上开始使" +"用 Mercurial 变得很容易。" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:15 +msgid "Windows" +msgstr "Windows" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:17 +msgid "" +"The best version of Mercurial for Windows is TortoiseHg, which can be found " +"at <ulink url=\"http://bitbucket.org/tortoisehg/stable/wiki/Home\">http://" +"bitbucket.org/tortoisehg/stable/wiki/Home</ulink>. This package has no " +"external dependencies; it <quote>just works</quote>. It provides both " +"command line and graphical user interfaces." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:27 +msgid "Mac OS X" +msgstr "Mac OS X" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:29 +msgid "" +"Lee Cantey publishes an installer of Mercurial for Mac OS X at <ulink url=" +"\"http://mercurial.berkwood.com\">http://mercurial.berkwood.com</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:35 +msgid "Linux" +msgstr "Linux" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:37 +msgid "" +"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." +msgstr "" +"由于每种 Linux 发行版都有自己的包管理工具,开发策略和进度,从而很难给出安装 " +"Mercurial 二进制包的全面说明。你安装的 Mercurial 版本,在很大程度上依赖于你所" +"使用的发行版的 Mercurial 维护者的活跃程度。" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:44 +msgid "" +"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 <literal>mercurial</literal>." +msgstr "" +"为了让事情简单,我会致力于说明在最流行的 Linux 发行版中,从命令行安装 " +"Mercurial 的方法。这些发行版都提供了图形界面的包管理器,让你通过点击鼠标安装 " +"Mercurial;寻找的包名称是 <literal>mercurial</literal>。" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:52 +msgid "Ubuntu and Debian:" +msgstr "Ubuntu 与 Debian:" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:54 +msgid "Fedora and OpenSUSE:" +msgstr "Fedora and OpenSUSE:" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:56 +msgid "Gentoo:" +msgstr "Gentoo:" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:62 +msgid "Solaris" +msgstr "Solaris" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:64 +msgid "" +"SunFreeWare, at <ulink url=\"http://www.sunfreeware.com\">http://www." +"sunfreeware.com</ulink>, provides prebuilt packages of Mercurial." +msgstr "" +"位于 <ulink url=\"http://www.sunfreeware.com\">http://www.sunfreeware.com</" +"ulink> 的 SunFreeWare 提供了 Mercurial 的二进制安装包。" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:73 +msgid "Getting started" +msgstr "开始" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:75 +msgid "" +"To begin, we'll use the <command role=\"hg-cmd\">hg version</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:84 +msgid "Built-in help" +msgstr "内置帮助" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:86 +msgid "" +"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 <command role=\"hg-cmd\">hg help</" +"command>; 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:97 +msgid "" +"For a more impressive level of detail (which you won't usually need) run " +"<command role=\"hg-cmd\">hg help <option role=\"hg-opt-global\">-v</option></" +"command>. The <option role=\"hg-opt-global\">-v</option> option is short for " +"<option role=\"hg-opt-global\">--verbose</option>, and tells Mercurial to " +"print more information than it usually would." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:108 +msgid "Working with a repository" +msgstr "使用版本库" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:110 +msgid "" +"In Mercurial, everything happens inside a <emphasis>repository</emphasis>. " +"The repository for a project contains all of the files that <quote>belong to</" +"quote> that project, along with a historical record of the project's files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:116 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:123 +msgid "Making a local copy of a repository" +msgstr "创建版本库的工作副本" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:125 +msgid "" +"<emphasis>Copying</emphasis> 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 <command role=\"hg-cmd\">hg clone</command>, because " +"it makes an identical copy of an existing repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:134 +msgid "" +"One advantage of using <command role=\"hg-cmd\">hg clone</command> is that, " +"as we can see above, it lets us clone repositories over the network. Another " +"is that it remembers where we cloned from, which we'll find useful soon when " +"we want to fetch new changes from another repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:140 +msgid "" +"If our clone succeeded, we should now have a local directory called <filename " +"class=\"directory\">hello</filename>. This directory will contain some files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:146 +msgid "" +"These files have the same contents and history in our repository as they do " +"in the repository we cloned." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:149 +msgid "" +"Every Mercurial repository is complete, self-contained, and independent. It " +"contains its own private copy of a project's files and history. As we just " +"mentioned, a cloned repository remembers the location of the repository it " +"was cloned from, but Mercurial will not communicate with that repository, or " +"any other, unless you tell it to." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:156 +msgid "" +"What this means for now is that we're free to experiment with our repository, " +"safe in the knowledge that it's a private <quote>sandbox</quote> that won't " +"affect anyone else." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:162 +msgid "What's in a repository?" +msgstr "什么是版本库?" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:164 +msgid "" +"When we take a more detailed look inside a repository, we can see that it " +"contains a directory named <filename class=\"directory\">.hg</filename>. " +"This is where Mercurial keeps all of its metadata for the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:171 +msgid "" +"The contents of the <filename class=\"directory\">.hg</filename> directory " +"and its subdirectories are private to Mercurial. Every other file and " +"directory in the repository is yours to do with as you please." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:177 +msgid "" +"To introduce a little terminology, the <filename class=\"directory\">.hg</" +"filename> directory is the <quote>real</quote> repository, and all of the " +"files and directories that coexist with it are said to live in the " +"<emphasis>working directory</emphasis>. An easy way to remember the " +"distinction is that the <emphasis>repository</emphasis> contains the " +"<emphasis>history</emphasis> of your project, while the <emphasis>working " +"directory</emphasis> contains a <emphasis>snapshot</emphasis> of your project " +"at a particular point in history." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:192 +msgid "A tour through history" +msgstr "回溯历史" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:194 +msgid "" +"One of the first things we might want to do with a new, unfamiliar repository " +"is understand its history. The <command role=\"hg-cmd\">hg log</command> " +"command gives us a view of the history of changes in the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:201 +msgid "" +"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 <emphasis>changeset</emphasis>, because it can " +"contain a record of changes to several files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:207 +msgid "" +"The fields in a record of output from <command role=\"hg-cmd\">hg log</" +"command> are as follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:211 +msgid "" +"<literal>changeset</literal>: This field has the format of a number, followed " +"by a colon, followed by a hexadecimal (or <emphasis>hex</emphasis>) string. " +"These are <emphasis>identifiers</emphasis> for the changeset. The hex string " +"is a unique identifier: the same hex string will always refer to the same " +"changeset. The number is shorter and easier to type than the hex string, but " +"it isn't unique: the same number in two different clones of a repository may " +"identify different changesets. Why provide the number at all, then? For " +"local convenience." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:223 +msgid "" +"<literal>user</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:227 +msgid "" +"<literal>date</literal>: 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:232 +msgid "" +"<literal>summary</literal>: The first line of the text message that the " +"creator of the changeset entered to describe the changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:236 +msgid "" +"Some changesets, such as the first in the list above, have a <literal>tag</" +"literal> field. A tag is another way to identify a changeset, by giving it " +"an easy-to-remember name. (The tag named <literal>tip</literal> is special: " +"it always refers to the newest change in a repository.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:244 +msgid "" +"The default output printed by <command role=\"hg-cmd\">hg log</command> is " +"purely a summary; it is missing a lot of detail." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:248 +msgid "" +"<xref linkend=\"fig:tour-basic:history\"/> provides a graphical " +"representation of the history of the <filename class=\"directory\">hello</" +"filename> repository, to make it a little easier to see which direction " +"history is <quote>flowing</quote> in. We'll be returning to this figure " +"several times in this chapter and the chapter that follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><figure><title> +#: ../en/ch01-tour-basic.xml:257 +msgid "" +"Graphical history of the <filename class=\"directory\">hello</filename> " +"repository" +msgstr "版本库 <filename class=\"directory\">hello</filename> 的历史图" + +#. type: Content of: <book><chapter><sect1><figure> +#: ../en/ch01-tour-basic.xml:259 ../en/ch02-tour-merge.xml:50 +#: ../en/ch02-tour-merge.xml:181 ../en/ch03-concepts.xml:293 +msgid "<placeholder type=\"mediaobject\" id=\"0\"/>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><figure><mediaobject> +#: ../en/ch01-tour-basic.xml:260 +msgid "" +"<imageobject><imagedata fileref=\"figs/tour-history.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject><textobject><phrase> +#: ../en/ch01-tour-basic.xml:261 ../en/ch02-tour-merge.xml:52 +#: ../en/ch02-tour-merge.xml:82 ../en/ch02-tour-merge.xml:129 +#: ../en/ch02-tour-merge.xml:183 ../en/ch02-tour-merge.xml:254 +#: ../en/ch03-concepts.xml:57 ../en/ch03-concepts.xml:106 +#: ../en/ch03-concepts.xml:191 ../en/ch03-concepts.xml:295 +#: ../en/ch03-concepts.xml:346 ../en/ch03-concepts.xml:361 +#: ../en/ch03-concepts.xml:402 ../en/ch03-concepts.xml:422 +#: ../en/ch03-concepts.xml:465 ../en/ch05-collab.xml:276 +#: ../en/ch08-undo.xml:365 ../en/ch08-undo.xml:412 ../en/ch08-undo.xml:477 +#: ../en/ch08-undo.xml:515 ../en/ch11-mq.xml:412 +msgid "XXX add text" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:266 +msgid "Changesets, revisions, and talking to other people" +msgstr "修改集,版本,与其它用户交互" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:269 +msgid "" +"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 <quote>changeset</quote> is often compressed to " +"<quote>change</quote> or (when written) <quote>cset</quote>, and sometimes a " +"changeset is referred to as a <quote>revision</quote> or a <quote>rev</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:279 +msgid "" +"While it doesn't matter what <emphasis>word</emphasis> you use to refer to " +"the concept of <quote>a changeset</quote>, the <emphasis>identifier</" +"emphasis> that you use to refer to <quote>a <emphasis>specific</emphasis> " +"changeset</quote> is of great importance. Recall that the <literal>changeset</" +"literal> field in the output from <command role=\"hg-cmd\">hg log</command> " +"identifies a changeset using both a number and a hexadecimal string." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:288 +msgid "" +"The revision number is a handy notation that is <emphasis>only valid in that " +"repository</emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:291 +msgid "" +"The hexadecimal string is the <emphasis>permanent, unchanging identifier</" +"emphasis> that will always identify that exact changeset in <emphasis>every</" +"emphasis> copy of the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:297 +msgid "" +"This distinction is important. If you send someone an email talking about " +"<quote>revision 33</quote>, there's a high likelihood that their revision 33 " +"will <emphasis>not be the same</emphasis> 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 <literal>a,b,c</" +"literal> can easily appear in one repository as <literal>0,1,2</literal>, " +"while in another as <literal>0,2,1</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:309 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:317 +msgid "Viewing specific revisions" +msgstr "察看指定版本" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:319 +msgid "" +"To narrow the output of <command role=\"hg-cmd\">hg log</command> down to a " +"single revision, use the <option role=\"hg-opt-log\">-r</option> (or <option " +"role=\"hg-opt-log\">--rev</option>) option. You can use either a revision " +"number or a hexadecimal identifier, and you can provide as many revisions as " +"you want." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:328 +msgid "" +"If you want to see the history of several revisions without having to list " +"each one, you can use <emphasis>range notation</emphasis>; this lets you " +"express the idea <quote>I want all revisions between <literal>abc</literal> " +"and <literal>def</literal>, inclusive</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:336 +msgid "" +"Mercurial also honours the order in which you specify revisions, so <command " +"role=\"hg-cmd\">hg log -r 2:4</command> prints 2, 3, and 4. while <command " +"role=\"hg-cmd\">hg log -r 4:2</command> prints 4, 3, and 2." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:343 +msgid "More detailed information" +msgstr "更详细的信息" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:345 +msgid "" +"While the summary information printed by <command role=\"hg-cmd\">hg log</" +"command> 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 <command role=\"hg-cmd\">hg log</command> command's <option role=\"hg-opt-" +"global\">-v</option> (or <option role=\"hg-opt-global\">--verbose</option>) " +"option gives you this extra detail." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:357 +msgid "" +"If you want to see both the description and content of a change, add the " +"<option role=\"hg-opt-log\">-p</option> (or <option role=\"hg-opt-log\">--" +"patch</option>) option. This displays the content of a change as a " +"<emphasis>unified diff</emphasis> (if you've never seen a unified diff " +"before, see <xref linkend=\"sec:mq:patch\"/> for an overview)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:367 +msgid "" +"The <option role=\"hg-opt-log\">-p</option> option is tremendously useful, so " +"it's well worth remembering." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:374 +msgid "All about command options" +msgstr "命令选项" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:376 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:380 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:387 +msgid "" +"Every option has a long name. For example, as we've already seen, the " +"<command role=\"hg-cmd\">hg log</command> command accepts a <option role=\"hg-" +"opt-log\">--rev</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:393 +msgid "" +"Most options have short names, too. Instead of <option role=\"hg-opt-log\">--" +"rev</option>, we can use <option role=\"hg-opt-log\">-r</option>. (The " +"reason that some options don't have short names is that the options in " +"question are rarely used.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:400 +msgid "" +"Long options start with two dashes (e.g. <option role=\"hg-opt-log\">--rev</" +"option>), while short options start with one (e.g. <option role=\"hg-opt-log" +"\">-r</option>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:406 +msgid "" +"Option naming and usage is consistent across commands. For example, every " +"command that lets you specify a changeset ID or revision number accepts both " +"<option role=\"hg-opt-log\">-r</option> and <option role=\"hg-opt-log\">--" +"rev</option> arguments." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:413 +msgid "" +"If you are using short options, you can save typing by running them together. " +"For example, the command <command role=\"hg-cmd\">hg log -v -p -r 2</command> " +"can be written as <command role=\"hg-cmd\">hg log -vpr2</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:420 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:424 +msgid "" +"Most commands that print output of some kind will print more output when " +"passed a <option role=\"hg-opt-global\">-v</option> (or <option role=\"hg-opt-" +"global\">--verbose</option>) option, and less when passed <option role=\"hg-" +"opt-global\">-q</option> (or <option role=\"hg-opt-global\">--quiet</option>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><title> +#: ../en/ch01-tour-basic.xml:431 +msgid "Option naming consistency" +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch01-tour-basic.xml:433 +msgid "" +"Almost always, Mercurial commands use consistent option names to refer to the " +"same concepts. For instance, if a command deals with changesets, you'll " +"always identify them with <option role=\"hg-opt-log\">--rev</option> or " +"<option role=\"hg-opt-log\">-r</option>. This consistent use of option names " +"makes it easier to remember what options a particular command takes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:444 +msgid "Making and reviewing changes" +msgstr "创建和复审修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:446 +msgid "" +"Now that we have a grasp of viewing history in Mercurial, let's take a look " +"at making some changes and examining them." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:450 +msgid "" +"The first thing we'll do is isolate our experiment in a repository of its " +"own. We use the <command role=\"hg-cmd\">hg clone</command> 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<placeholder type=\"footnote\" id=\"0\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para><footnote><para> +#: ../en/ch01-tour-basic.xml:457 +msgid "" +"The saving of space arises when source and destination repositories are on " +"the same filesystem, in which case Mercurial will use hardlinks to do copy-on-" +"write sharing of its internal metadata. If that explanation meant nothing to " +"you, don't worry: everything happens transparently and automatically, and you " +"don't need to understand it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:467 +msgid "" +"As an aside, it's often good practice to keep a <quote>pristine</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:476 +msgid "" +"In our <filename class=\"directory\">my-hello</filename> repository, we have " +"a file <filename>hello.c</filename> that contains the classic <quote>hello, " +"world</quote> program." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:482 +msgid "Let's edit this file so that it prints a second line of output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:487 +msgid "" +"Mercurial's <command role=\"hg-cmd\">hg status</command> command will tell us " +"what Mercurial knows about the files in the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:493 +msgid "" +"The <command role=\"hg-cmd\">hg status</command> command prints no output for " +"some files, but a line starting with <quote><literal>M</literal></quote> for " +"<filename>hello.c</filename>. Unless you tell it to, <command role=\"hg-cmd" +"\">hg status</command> will not print any output for files that have not been " +"modified." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:500 +msgid "" +"The <quote><literal>M</literal></quote> indicates that Mercurial has noticed " +"that we modified <filename>hello.c</filename>. We didn't need to " +"<emphasis>inform</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:508 +msgid "" +"It's somewhat helpful to know that we've modified <filename>hello.c</" +"filename>, but we might prefer to know exactly <emphasis>what</emphasis> " +"changes we've made to it. To do this, we use the <command role=\"hg-cmd\">hg " +"diff</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:517 ../en/ch11-mq.xml:187 +msgid "Understanding patches" +msgstr "理解补丁" + +#. type: Content of: <book><chapter><sect1><tip><para> +#: ../en/ch01-tour-basic.xml:519 +msgid "" +"Remember to take a look at <xref linkend=\"sec:mq:patch\"/> if you don't know " +"how to read output above." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:525 +msgid "Recording changes in a new changeset" +msgstr "在新修改集中记录修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:527 +msgid "" +"We can modify files, build and test our changes, and use <command role=\"hg-" +"cmd\">hg status</command> and <command role=\"hg-cmd\">hg diff</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:534 +msgid "" +"The <command role=\"hg-cmd\">hg commit</command> command lets us create a new " +"changeset; we'll usually refer to this as <quote>making a commit</quote> or " +"<quote>committing</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:540 +msgid "Setting up a username" +msgstr "配置用户名称" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:542 +msgid "" +"When you try to run <command role=\"hg-cmd\">hg commit</command> 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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:551 +msgid "" +"If you specify a <option role=\"hg-opt-commit\">-u</option> option to the " +"<command role=\"hg-cmd\">hg commit</command> command on the command line, " +"followed by a username, this is always given the highest precedence." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:556 +msgid "" +"If you have set the <envar>HGUSER</envar> environment variable, this is " +"checked next." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:559 +msgid "" +"If you create a file in your home directory called <filename role=\"special" +"\">.hgrc</filename>, with a <envar role=\"rc-item-ui\">username</envar> " +"entry, that will be used next. To see what the contents of this file should " +"look like, refer to <xref linkend=\"sec:tour-basic:username\"/> below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:567 +msgid "" +"If you have set the <envar>EMAIL</envar> environment variable, this will be " +"used next." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch01-tour-basic.xml:570 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:577 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:581 +msgid "" +"You should think of the <envar>HGUSER</envar> environment variable and the " +"<option role=\"hg-opt-commit\">-u</option> option to the <command role=\"hg-" +"cmd\">hg commit</command> command as ways to <emphasis>override</emphasis> " +"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 <filename " +"role=\"special\">.hgrc</filename> file; see below for details." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch01-tour-basic.xml:590 +msgid "Creating a Mercurial configuration file" +msgstr "创建 Mercurial 的配置文件" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch01-tour-basic.xml:592 +msgid "" +"To set a user name, use your favourite editor to create a file called " +"<filename role=\"special\">.hgrc</filename> in your home directory. " +"Mercurial will use this file to look up your personalised configuration " +"settings. The initial contents of your <filename role=\"special\">.hgrc</" +"filename> should look like this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><remark> +#: ../en/ch01-tour-basic.xml:600 +msgid "Figure out what the appropriate directory is on Windows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch01-tour-basic.xml:607 +msgid "" +"The <quote><literal>[ui]</literal></quote> line begins a <emphasis>section</" +"emphasis> of the config file, so you can read the <quote><literal>username " +"= ...</literal></quote> line as meaning <quote>set the value of the " +"<literal>username</literal> item in the <literal>ui</literal> section</" +"quote>. A section continues until a new section begins, or the end of the " +"file. Mercurial ignores empty lines and treats any text from " +"<quote><literal>#</literal></quote> to the end of a line as a comment." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch01-tour-basic.xml:620 +msgid "Choosing a user name" +msgstr "选择用户名称" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch01-tour-basic.xml:622 +msgid "" +"You can use any text you like as the value of the <literal>username</literal> " +"config item, since this information is for reading by other people, but will " +"not be interpreted by Mercurial. The convention that most people follow is " +"to use their name and email address, as in the example above." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><note><para> +#: ../en/ch01-tour-basic.xml:629 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:639 +msgid "Writing a commit message" +msgstr "写提交日志" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:641 +msgid "" +"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 <emphasis>commit message</emphasis>. It will be a record " +"for readers of what we did and why, and it will be printed by <command role=" +"\"hg-cmd\">hg log</command> after we've finished committing." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:651 +msgid "" +"The editor that the <command role=\"hg-cmd\">hg commit</command> command " +"drops us into will contain an empty line or two, followed by a number of " +"lines starting with <quote><literal>HG:</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:665 +msgid "" +"Mercurial ignores the lines that start with <quote><literal>HG:</literal></" +"quote>; it uses them only to tell us which files it's recording changes to. " +"Modifying or deleting these lines has no effect." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:671 +msgid "Writing a good commit message" +msgstr "写好提交日志" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:673 +msgid "" +"Since <command role=\"hg-cmd\">hg log</command> 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 " +"<emphasis>doesn't</emphasis> follow this guideline, and hence has a summary " +"that is not readable." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:687 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:693 +msgid "" +"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 <command role=\"hg-cmd\">hg log --patch</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:700 +msgid "Aborting a commit" +msgstr "终止提交" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:702 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:707 +msgid "" +"If we run the <command role=\"hg-cmd\">hg commit</command> command without " +"any arguments, it records all of the changes we've made, as reported by " +"<command role=\"hg-cmd\">hg status</command> and <command role=\"hg-cmd\">hg " +"diff</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:714 +msgid "Admiring our new handiwork" +msgstr "欣赏我们的成果" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:716 +msgid "" +"Once we've finished the commit, we can use the <command role=\"hg-cmd\">hg " +"tip</command> command to display the changeset we just created. This command " +"produces output that is identical to <command role=\"hg-cmd\">hg log</" +"command>, but it only displays the newest revision in the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:725 +msgid "" +"We refer to the newest revision in the repository as the <emphasis>tip " +"revision</emphasis>, or simply the <emphasis>tip</emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:729 +msgid "" +"By the way, the <command role=\"hg-cmd\">hg tip</command> command accepts " +"many of the same options as <command role=\"hg-cmd\">hg log</command>, so " +"<option role=\"hg-opt-global\">-v</option> above indicates <quote>be verbose</" +"quote>, <option role=\"hg-opt-tip\">-p</option> specifies <quote>print a " +"patch</quote>. The use of <option role=\"hg-opt-tip\">-p</option> to print " +"patches is another example of the consistent naming we mentioned earlier." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch01-tour-basic.xml:741 +msgid "Sharing changes" +msgstr "共享修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch01-tour-basic.xml:743 +msgid "" +"We mentioned earlier that repositories in Mercurial are self-contained. This " +"means that the changeset we just created exists only in our <filename class=" +"\"directory\">my-hello</filename> repository. Let's look at a few ways that " +"we can propagate this change into other repositories." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:751 +msgid "Pulling changes from another repository" +msgstr "从其它版本库取得修改" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:752 +msgid "" +"To get started, let's clone our original <filename class=\"directory\">hello</" +"filename> repository, which does not contain the change we just committed. " +"We'll call our temporary repository <filename class=\"directory\">hello-pull</" +"filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:760 +msgid "" +"We'll use the <command role=\"hg-cmd\">hg pull</command> command to bring " +"changes from <filename class=\"directory\">my-hello</filename> into <filename " +"class=\"directory\">hello-pull</filename>. However, blindly pulling unknown " +"changes into a repository is a somewhat scary prospect. Mercurial provides " +"the <command role=\"hg-cmd\">hg incoming</command> command to tell us what " +"changes the <command role=\"hg-cmd\">hg pull</command> command " +"<emphasis>would</emphasis> pull into the repository, without actually pulling " +"the changes in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:773 +msgid "" +"Suppose you're pulling changes from a repository on the network somewhere. " +"While you are looking at the <command role=\"hg-cmd\">hg incoming</command> " +"output, and before you pull those changes, someone might have committed " +"something in the remote repository. This means that it's possible to pull " +"more changes than you saw when using <command role=\"hg-cmd\">hg incoming</" +"command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:781 +msgid "" +"Bringing changes into a repository is a simple matter of running the <command " +"role=\"hg-cmd\">hg pull</command> command, and telling it which repository to " +"pull from." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:788 +msgid "" +"As you can see from the before-and-after output of <command role=\"hg-cmd" +"\">hg tip</command>, we have successfully pulled changes into our " +"repository. There remains one step before we can see these changes in the " +"working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:796 +msgid "Updating the working directory" +msgstr "更新工作目录" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:798 +msgid "" +"We have so far glossed over the relationship between a repository and its " +"working directory. The <command role=\"hg-cmd\">hg pull</command> command " +"that we ran in <xref linkend=\"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 <command role=\"hg-cmd\">hg pull</command> does " +"not (by default) touch the working directory. Instead, we use the <command " +"role=\"hg-cmd\">hg update</command> command to do this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:810 +msgid "" +"It might seem a bit strange that <command role=\"hg-cmd\">hg pull</command> " +"doesn't update the working directory automatically. There's actually a good " +"reason for this: you can use <command role=\"hg-cmd\">hg update</command> to " +"update the working directory to the state it was in at <emphasis>any " +"revision</emphasis> in the history of the repository. If you had the working " +"directory updated to an old revision&emdash;to hunt down the origin of a bug, " +"say&emdash;and ran a <command role=\"hg-cmd\">hg pull</command> which " +"automatically updated the working directory to a new revision, you might not " +"be terribly happy." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:821 +msgid "" +"However, since pull-then-update is such a common thing to do, Mercurial lets " +"you combine the two by passing the <option role=\"hg-opt-pull\">-u</option> " +"option to <command role=\"hg-cmd\">hg pull</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:826 +msgid "" +"If you look back at the output of <command role=\"hg-cmd\">hg pull</command> " +"in <xref linkend=\"sec:tour:pull\"/> when we ran it without <option role=\"hg-" +"opt-pull\">-u</option>, you can see that it printed a helpful reminder that " +"we'd have to take an explicit step to update the working directory:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:835 +msgid "" +"To find out what revision the working directory is at, use the <command role=" +"\"hg-cmd\">hg parents</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:841 +msgid "" +"If you look back at <xref linkend=\"fig:tour-basic:history\"/>, you'll see " +"arrows connecting each changeset. The node that the arrow leads " +"<emphasis>from</emphasis> in each case is a parent, and the node that the " +"arrow leads <emphasis>to</emphasis> is its child. The working directory has " +"a parent in just the same way; this is the changeset that the working " +"directory currently contains." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:850 +msgid "" +"To update the working directory to a particular revision, give a revision " +"number or changeset ID to the <command role=\"hg-cmd\">hg update</command> " +"command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:857 +msgid "" +"If you omit an explicit revision, <command role=\"hg-cmd\">hg update</" +"command> will update to the tip revision, as shown by the second call to " +"<command role=\"hg-cmd\">hg update</command> in the example above." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:865 +msgid "Pushing changes to another repository" +msgstr "发布修改到其它版本库" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:867 +msgid "" +"Mercurial lets us push changes to another repository, from the repository " +"we're currently visiting. As with the example of <command role=\"hg-cmd\">hg " +"pull</command> above, we'll create a temporary repository to push our changes " +"into." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:875 +msgid "" +"The <command role=\"hg-cmd\">hg outgoing</command> command tells us what " +"changes would be pushed into another repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:881 +msgid "" +"And the <command role=\"hg-cmd\">hg push</command> command does the actual " +"push." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:887 +msgid "" +"As with <command role=\"hg-cmd\">hg pull</command>, the <command role=\"hg-cmd" +"\">hg push</command> command does not update the working directory in the " +"repository that it's pushing changes into. Unlike <command role=\"hg-cmd\">hg " +"pull</command>, <command role=\"hg-cmd\">hg push</command> does not provide a " +"<literal>-u</literal> option that updates the other repository's working " +"directory. This asymmetry is deliberate: the repository we're pushing to " +"might be on a remote server and shared between several people. If we were to " +"update its working directory while someone was working in it, their work " +"would be disrupted." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:899 +msgid "" +"What happens if we try to pull or push changes and the receiving repository " +"already has those changes? Nothing too exciting." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch01-tour-basic.xml:906 +msgid "Sharing changes over a network" +msgstr "通过网络共享修改" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:908 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch01-tour-basic.xml:916 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch02-tour-merge.xml:5 +msgid "A tour of Mercurial: merging work" +msgstr "Mercurial 教程: 合并工作" + +#. type: Content of: <book><chapter><para> +#: ../en/ch02-tour-merge.xml:7 +msgid "" +"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 <emphasis>merging</emphasis> changes from separate repositories." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch02-tour-merge.xml:13 +msgid "Merging streams of work" +msgstr "合并的流程" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:15 +msgid "" +"Merging is a fundamental part of working with a distributed revision control " +"tool." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch02-tour-merge.xml:18 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch02-tour-merge.xml:24 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:30 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:37 +msgid "" +"We should now have two copies of <filename>hello.c</filename> with different " +"contents. The histories of the two repositories have also diverged, as " +"illustrated in <xref linkend=\"fig:tour-merge:sep-repos\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><figure><title> +#: ../en/ch02-tour-merge.xml:46 +msgid "" +"Divergent recent histories of the <filename class=\"directory\">my-hello</" +"filename> and <filename class=\"directory\">my-new-hello</filename> " +"repositories" +msgstr "" +"<filename class=\"directory\">my-hello</filename> 与 <filename class=" +"\"directory\">my-new-hello</filename> 最新的历史分叉" + +#. type: Content of: <book><chapter><sect1><figure><mediaobject> +#: ../en/ch02-tour-merge.xml:51 +msgid "" +"<imageobject><imagedata fileref=\"figs/tour-merge-sep-repos.png\"/></" +"imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:56 +msgid "" +"We already know that pulling changes from our <filename class=\"directory" +"\">my-hello</filename> repository will have no effect on the working " +"directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:62 +msgid "" +"However, the <command role=\"hg-cmd\">hg pull</command> command says " +"something about <quote>heads</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch02-tour-merge.xml:66 +msgid "Head changesets" +msgstr "顶点修改集" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:68 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch02-tour-merge.xml:75 +msgid "" +"Repository contents after pulling from <filename class=\"directory\">my-" +"hello</filename> into <filename class=\"directory\">my-new-hello</filename>" +msgstr "" +"从 <filename class=\"directory\">my-hello</filename> 拉到 <filename class=" +"\"directory\">my-new-hello</filename> 之后版本库的内容" + +#. type: Content of: <book><chapter><sect1><sect2><figure> +#: ../en/ch02-tour-merge.xml:78 ../en/ch02-tour-merge.xml:125 +#: ../en/ch02-tour-merge.xml:250 ../en/ch03-concepts.xml:55 +#: ../en/ch03-concepts.xml:104 ../en/ch03-concepts.xml:189 +#: ../en/ch03-concepts.xml:344 ../en/ch03-concepts.xml:359 +#: ../en/ch03-concepts.xml:400 ../en/ch03-concepts.xml:420 +#: ../en/ch03-concepts.xml:461 ../en/ch05-collab.xml:274 +#: ../en/ch08-undo.xml:363 ../en/ch08-undo.xml:410 ../en/ch08-undo.xml:475 +#: ../en/ch08-undo.xml:513 ../en/ch11-mq.xml:410 +msgid " <placeholder type=\"mediaobject\" id=\"0\"/>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch02-tour-merge.xml:79 +msgid "" +"<imageobject> <imagedata fileref=\"figs/tour-merge-pull.png\"/> </imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:86 +msgid "" +"In <xref linkend=\"fig:tour-merge:pull\"/>, you can see the effect of the " +"pull from <filename class=\"directory\">my-hello</filename> into <filename " +"class=\"directory\">my-new-hello</filename>. The history that was already " +"present in <filename class=\"directory\">my-new-hello</filename> is " +"untouched, but a new revision has been added. By referring to <xref linkend=" +"\"fig:tour-merge:sep-repos\"/>, we can see that the <emphasis>changeset ID</" +"emphasis> remains the same in the new repository, but the <emphasis>revision " +"number</emphasis> 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 <command role=\"hg-cmd\">hg heads</" +"command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch02-tour-merge.xml:105 +msgid "Performing the merge" +msgstr "执行合并" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:107 +msgid "" +"What happens if we try to use the normal <command role=\"hg-cmd\">hg update</" +"command> command to update to the new tip?" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:113 +msgid "" +"Mercurial is telling us that the <command role=\"hg-cmd\">hg update</command> " +"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 <command role=\"hg-cmd\">hg merge</command> command to " +"merge the two heads." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch02-tour-merge.xml:123 +msgid "Working directory and repository during merge, and following commit" +msgstr "在合并期间,以及提交之后的工作目录与版本库" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch02-tour-merge.xml:126 +msgid "" +"<imageobject> <imagedata fileref=\"figs/tour-merge-merge.png\"/> </" +"imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:133 +msgid "" +"This updates the working directory so that it contains changes from " +"<emphasis>both</emphasis> heads, which is reflected in both the output of " +"<command role=\"hg-cmd\">hg parents</command> and the contents of " +"<filename>hello.c</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch02-tour-merge.xml:143 +msgid "Committing the results of the merge" +msgstr "提交合并结果" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:145 +msgid "" +"Whenever we've done a merge, <command role=\"hg-cmd\">hg parents</command> " +"will display two parents until we <command role=\"hg-cmd\">hg commit</" +"command> the results of the merge." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:152 +msgid "" +"We now have a new tip revision; notice that it has <emphasis>both</emphasis> " +"of our former heads as its parents. These are the same revisions that were " +"previously displayed by <command role=\"hg-cmd\">hg parents</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:159 +msgid "" +"In <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch02-tour-merge.xml:170 +msgid "Merging conflicting changes" +msgstr "合并有冲突的改变" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:172 +msgid "" +"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 <emphasis>conflict</emphasis>, " +"where you have to decide how to reconcile the different changes into " +"something coherent." +msgstr "" + +#. type: Content of: <book><chapter><sect1><figure><title> +#: ../en/ch02-tour-merge.xml:180 +msgid "Conflicting changes to a document" +msgstr "冲突的修改" + +#. type: Content of: <book><chapter><sect1><figure><mediaobject> +#: ../en/ch02-tour-merge.xml:182 +msgid "" +"<imageobject><imagedata fileref=\"figs/tour-merge-conflict.png\"/></" +"imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:187 +msgid "" +"<xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:194 +msgid "" +"Mercurial doesn't have a built-in facility for handling conflicts. Instead, " +"it runs an external program called <command>hgmerge</command>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:205 +msgid "" +"It's also possible to get Mercurial to run another program or script instead " +"of <command>hgmerge</command>, by setting the <envar>HGMERGE</envar> " +"environment variable to the name of your preferred program." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch02-tour-merge.xml:211 +msgid "Using a graphical merge tool" +msgstr "使用图形合并工具" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:213 +msgid "" +"My preferred graphical merge tool is <command>kdiff3</command>, which I'll " +"use to describe the features that are common to graphical file merging " +"tools. You can see a screenshot of <command>kdiff3</command> in action in " +"<xref linkend=\"fig:tour-merge:kdiff3\"/>. The kind of merge it is " +"performing is called a <emphasis>three-way merge</emphasis>, 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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch02-tour-merge.xml:223 +msgid "" +"At the left is the <emphasis>base</emphasis> version of the file, i.e. the " +"most recent version from which the two versions we're trying to merge are " +"descended." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch02-tour-merge.xml:228 +msgid "" +"In the middle is <quote>our</quote> version of the file, with the contents " +"that we modified." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch02-tour-merge.xml:231 +msgid "" +"On the right is <quote>their</quote> version of the file, the one that from " +"the changeset that we're trying to merge with." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:235 +msgid "" +"In the pane below these is the current <emphasis>result</emphasis> of the " +"merge. Our task is to replace all of the red text, which indicates unresolved " +"conflicts, with some sensible merger of the <quote>ours</quote> and " +"<quote>theirs</quote> versions of the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:242 +msgid "" +"All four of these panes are <emphasis>locked together</emphasis>; if we " +"scroll vertically or horizontally in any of them, the others are updated to " +"display the corresponding sections of their respective files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch02-tour-merge.xml:248 +msgid "Using <command>kdiff3</command> to merge versions of a file" +msgstr "使用 <command>kdiff3</command> 合并文件的不同版本" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch02-tour-merge.xml:251 +msgid "" +"<imageobject> <imagedata width=\"100%\" fileref=\"figs/kdiff3.png\"/></" +"imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:259 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:265 +msgid "" +"There are <emphasis>many</emphasis> 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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch02-tour-merge.xml:274 +msgid "A worked example" +msgstr "合并实例" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:276 +msgid "" +"In this example, we will reproduce the file modification history of <xref " +"linkend=\"fig:tour-merge:conflict\"/> above. Let's begin by creating a " +"repository with a base version of our document." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:283 +msgid "We'll clone the repository and make a change to the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:288 +msgid "" +"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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:296 +msgid "" +"Having created two different versions of the file, we'll set up an " +"environment suitable for running our merge." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:302 +msgid "" +"In this example, I won't use Mercurial's normal <command>hgmerge</command> " +"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</envar> to tell Mercurial to use the non-interactive " +"<command>merge</command> command. This is bundled with many Unix-like " +"systems. If you're following this example on your computer, don't bother " +"setting <envar>HGMERGE</envar>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:312 +msgid "<emphasis role=\"bold\">XXX FIX THIS EXAMPLE.</emphasis>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:317 +msgid "" +"Because <command>merge</command> can't resolve the conflicting changes, it " +"leaves <emphasis>merge markers</emphasis> inside the file that has conflicts, " +"indicating which lines have conflicts, and whether they came from our version " +"of the file or theirs." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:323 +msgid "" +"Mercurial can tell from the way <command>merge</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch02-tour-merge.xml:330 +msgid "" +"If automatic or manual merges fail, there's nothing to prevent us from " +"<quote>fixing up</quote> the affected files ourselves, and committing the " +"results of our merge:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch02-tour-merge.xml:339 +msgid "Simplifying the pull-merge-commit sequence" +msgstr "简化拉-合并-提交程序" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:341 +msgid "" +"The process of merging changes as outlined above is straightforward, but " +"requires running three commands in sequence." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:347 +msgid "" +"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 " +"<quote>boilerplate</quote> text." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:351 +msgid "" +"It would be nice to reduce the number of steps needed, if this were " +"possible. Indeed, Mercurial is distributed with an extension called <literal " +"role=\"hg-ext\">fetch</literal> that does just this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:356 +msgid "" +"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 <quote>behind the scenes,</quote> for example adding " +"capabilities to the server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:363 +msgid "" +"The <literal role=\"hg-ext\">fetch</literal> extension adds a new command " +"called, not surprisingly, <command role=\"hg-cmd\">hg fetch</command>. This " +"extension acts as a combination of <command role=\"hg-cmd\">hg pull</" +"command>, <command role=\"hg-cmd\">hg update</command> and <command role=\"hg-" +"cmd\">hg merge</command>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:376 +msgid "" +"Enabling the <literal role=\"hg-ext\">fetch</literal> extension is easy. " +"Edit your <filename role=\"special\">.hgrc</filename>, and either go to the " +"<literal role=\"rc-extensions\">extensions</literal> section or create an " +"<literal role=\"rc-extensions\">extensions</literal> section. Then add a line " +"that simply reads <quote><literal>fetch </literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch02-tour-merge.xml:385 +msgid "" +"(Normally, on the right-hand side of the <quote><literal>=</literal></quote> " +"would appear the location of the extension, but since the <literal role=\"hg-" +"ext\">fetch</literal> extension is in the standard distribution, Mercurial " +"knows where to search for it.)" +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch03-concepts.xml:5 +msgid "Behind the scenes" +msgstr "Mercurial 内幕" + +#. type: Content of: <book><chapter><para> +#: ../en/ch03-concepts.xml:7 +msgid "" +"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 <quote>mental model</quote> of what's going on." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch03-concepts.xml:13 +msgid "" +"This understanding gives me confidence that Mercurial has been carefully " +"designed to be both <emphasis>safe</emphasis> and <emphasis>efficient</" +"emphasis>. 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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch03-concepts.xml:20 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch03-concepts.xml:25 +msgid "Mercurial's historical record" +msgstr "Mercurial 的历史记录" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:28 +msgid "Tracking the history of a single file" +msgstr "跟踪单一文件的历史" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:30 +msgid "" +"When Mercurial tracks modifications to a file, it stores the history of that " +"file in a metadata object called a <emphasis>filelog</emphasis>. 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 <filename " +"role=\"special\" class=\"directory\">.hg/store/data</filename> directory. A " +"filelog contains two kinds of information: revision data, and an index to " +"help Mercurial to find a revision efficiently." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:41 +msgid "" +"A file that is large, or has a lot of history, has its filelog stored in " +"separate data (<quote><literal>.d</literal></quote> suffix) and index " +"(<quote><literal>.i</literal></quote> suffix) files. For small files without " +"much history, the revision data and index are combined in a single " +"<quote><literal>.i</literal></quote> file. The correspondence between a file " +"in the working directory and the filelog that tracks its history in the " +"repository is illustrated in <xref linkend=\"fig:concepts:filelog\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:53 +msgid "" +"Relationships between files in working directory and filelogs in repository" +msgstr "工作目录中的文件与版本库中的文件日志之间的关系" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:56 +msgid "<imageobject><imagedata fileref=\"figs/filelog.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:63 +msgid "Managing tracked files" +msgstr "管理跟踪的文件" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:65 +msgid "" +"Mercurial uses a structure called a <emphasis>manifest</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:75 +msgid "Recording changeset information" +msgstr "记录修改集信息" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:77 +msgid "" +"The <emphasis>changelog</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:85 +msgid "Relationships between revisions" +msgstr "版本之间的关系" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:87 +msgid "" +"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 " +"<emphasis>across</emphasis> these structures, and they are hierarchical in " +"nature." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:94 +msgid "" +"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 <xref linkend=\"fig:concepts:metadata" +"\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:103 +msgid "Metadata relationships" +msgstr "元数据之间的关系" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:105 +msgid "<imageobject><imagedata fileref=\"figs/metadata.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:110 +msgid "" +"As the illustration shows, there is <emphasis>not</emphasis> a <quote>one to " +"one</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch03-concepts.xml:123 +msgid "Safe, efficient storage" +msgstr "安全,高效的存储" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:125 +msgid "" +"The underpinnings of changelogs, manifests, and filelogs are provided by a " +"single structure called the <emphasis>revlog</emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:130 +msgid "Efficient storage" +msgstr "高效存储" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:132 +msgid "" +"The revlog provides efficient storage of revisions using a <emphasis>delta</" +"emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:140 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:149 +msgid "Safe operation" +msgstr "安全操作" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:151 +msgid "" +"Mercurial only ever <emphasis>appends</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:157 +msgid "" +"In addition, Mercurial treats every write as part of a <emphasis>transaction</" +"emphasis> that can span a number of files. A transaction is " +"<emphasis>atomic</emphasis>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:167 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:174 +msgid "Fast retrieval" +msgstr "快速检索" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:176 +msgid "" +"Mercurial cleverly avoids a pitfall common to all earlier revision control " +"systems: the problem of <emphasis>inefficient retrieval</emphasis>. Most " +"revision control systems store the contents of a revision as an incremental " +"series of modifications against a <quote>snapshot</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:188 +msgid "Snapshot of a revlog, with incremental deltas" +msgstr "版本日志的快照,以及增量差异" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:190 +msgid "<imageobject><imagedata fileref=\"figs/snapshot.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:195 +msgid "" +"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 <emphasis>any</emphasis> revision of a file quickly. This " +"approach works so well that it has since been copied by several other " +"revision control systems." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:204 +msgid "" +"<xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch03-concepts.xml:210 +msgid "Aside: the influence of video compression" +msgstr "旁白: 视频压缩的影响" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:212 +msgid "" +"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 <quote>lossy</quote> " +"compression techniques to increase the compression ratio, so visual errors " +"accumulate over the course of a number of inter-frame deltas." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:221 +msgid "" +"Because it's possible for a video stream to <quote>drop out</quote> " +"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 <quote>key frame</quote>) 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:235 +msgid "Identification and strong integrity" +msgstr "鉴别和强完整性" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:237 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:242 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:249 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:254 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch03-concepts.xml:266 +msgid "Revision history, branching, and merging" +msgstr "修订历史,分支与合并" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:268 +msgid "" +"Every entry in a Mercurial revlog knows the identity of its immediate " +"ancestor revision, usually referred to as its <emphasis>parent</emphasis>. " +"In fact, a revision contains room for not one parent, but two. Mercurial " +"uses a special hash, called the <quote>null ID</quote>, to represent the idea " +"<quote>there is no parent here</quote>. This hash is simply a string of " +"zeroes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:276 +msgid "" +"In <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:282 +msgid "" +"The first revision in a revlog (at the bottom of the image) has the null ID " +"in both of its parent slots. For a <quote>normal</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><figure><title> +#: ../en/ch03-concepts.xml:292 +msgid "The conceptual structure of a revlog" +msgstr "版本日志的设计结构" + +#. type: Content of: <book><chapter><sect1><figure><mediaobject> +#: ../en/ch03-concepts.xml:294 +msgid "<imageobject><imagedata fileref=\"figs/revlog.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch03-concepts.xml:301 +msgid "The working directory" +msgstr "工作目录" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:303 +msgid "" +"In the working directory, Mercurial stores a snapshot of the files from the " +"repository as of a particular changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:306 +msgid "" +"The working directory <quote>knows</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:315 +msgid "" +"The <emphasis>dirstate</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:321 +msgid "" +"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 " +"<command role=\"hg-cmd\">hg update</command> command, the changeset that you " +"update to is stored in the <quote>first parent</quote> slot, and the null ID " +"in the second. When you <command role=\"hg-cmd\">hg merge</command> with " +"another changeset, the first parent remains unchanged, and the second parent " +"is filled in with the changeset you're merging with. The <command role=\"hg-" +"cmd\">hg parents</command> command tells you what the parents of the dirstate " +"are." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:335 +msgid "What happens when you commit" +msgstr "当你提交时发生的事情" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:337 +msgid "" +"The dirstate stores parent information for more than just book-keeping " +"purposes. Mercurial uses the parents of the dirstate as <emphasis>the " +"parents of a new changeset</emphasis> when you perform a commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:343 +msgid "The working directory can have two parents" +msgstr "工作目录可以有两个父亲" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:345 +msgid "<imageobject><imagedata fileref=\"figs/wdir.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:350 +msgid "" +"<xref linkend=\"fig:concepts:wdir\"/> shows the normal state of the working " +"directory, where it has a single changeset as parent. That changeset is the " +"<emphasis>tip</emphasis>, the newest changeset in the repository that has no " +"children." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:357 +msgid "The working directory gains new parents after a commit" +msgstr "提交之后,工作目录的父亲就改变了" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:360 +msgid "" +"<imageobject><imagedata fileref=\"figs/wdir-after-commit.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:365 +msgid "" +"It's useful to think of the working directory as <quote>the changeset I'm " +"about to commit</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:373 +msgid "" +"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 <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:384 +msgid "Creating a new head" +msgstr "创建新顶点" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:386 +msgid "" +"It's perfectly normal to update the working directory to a changeset other " +"than the current tip. For example, you might want to know what your project " +"looked like last Tuesday, or you could be looking through changesets to see " +"which one introduced a bug. In cases like this, the natural thing to do is " +"update the working directory to the changeset you're interested in, and then " +"examine the files in the working directory directly to see their contents as " +"they were when you committed that changeset. The effect of this is shown in " +"<xref linkend=\"fig:concepts:wdir-pre-branch\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:398 +msgid "The working directory, updated to an older changeset" +msgstr "同步到旧修改集的工作目录" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:401 +msgid "" +"<imageobject><imagedata fileref=\"figs/wdir-pre-branch.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:406 +msgid "" +"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 <emphasis>heads</emphasis>. You can see the structure that " +"this creates in <xref linkend=\"fig:concepts:wdir-branch\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:418 +msgid "After a commit made while synced to an older changeset" +msgstr "对同步到旧修改集的工作目录提交之后" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:421 +msgid "<imageobject><imagedata fileref=\"figs/wdir-branch.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch03-concepts.xml:427 +msgid "" +"If you're new to Mercurial, you should keep in mind a common <quote>error</" +"quote>, which is to use the <command role=\"hg-cmd\">hg pull</command> " +"command without any options. By default, the <command role=\"hg-cmd\">hg " +"pull</command> command <emphasis>does not</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch03-concepts.xml:439 +msgid "" +"I put the word <quote>error</quote> in quotes because all that you need to do " +"to rectify this situation is <command role=\"hg-cmd\">hg merge</command>, " +"then <command role=\"hg-cmd\">hg commit</command>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:451 +msgid "Merging heads" +msgstr "合并顶点" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:453 +msgid "" +"When you run the <command role=\"hg-cmd\">hg merge</command> 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 " +"<xref linkend=\"fig:concepts:wdir-merge\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch03-concepts.xml:460 +msgid "Merging two heads" +msgstr "合并两个顶点" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch03-concepts.xml:462 +msgid "" +"<imageobject> <imagedata fileref=\"figs/wdir-merge.png\"/> </imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:469 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:474 +msgid "If neither changeset has modified a file, do nothing with that file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:477 +msgid "" +"If one changeset has modified a file, and the other hasn't, create the " +"modified copy of the file in the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:481 +msgid "" +"If one changeset has removed a file, and the other hasn't (or has also " +"deleted it), delete the file from the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:485 +msgid "" +"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?" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:489 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch03-concepts.xml:494 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:498 +msgid "" +"There are more details&emdash;merging has plenty of corner cases&emdash;but " +"these are the most common choices that are involved in a merge. As you can " +"see, most cases are completely automatic, and indeed most merges finish " +"automatically, without requiring your input to resolve any conflicts." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:505 +msgid "" +"When you're thinking about what happens when you commit after a merge, once " +"again the working directory is <quote>the changeset I'm about to commit</" +"quote>. After the <command role=\"hg-cmd\">hg merge</command> command " +"completes, the working directory has two parents; these will become the " +"parents of the new changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:512 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch03-concepts.xml:523 +msgid "Other interesting design features" +msgstr "其它有趣的设计特性" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch03-concepts.xml:525 +msgid "" +"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 <quote>big ticket</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:536 +msgid "Clever compression" +msgstr "智能压缩" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:538 +msgid "" +"When appropriate, Mercurial will store both snapshots and deltas in " +"compressed form. It does this by always <emphasis>trying to</emphasis> " +"compress a snapshot or delta, but only storing the compressed version if it's " +"smaller than the uncompressed version." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:544 +msgid "" +"This means that Mercurial does <quote>the right thing</quote> when storing a " +"file whose native form is compressed, such as a <literal>zip</literal> " +"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 <literal>zip</literal> or JPEG." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:552 +msgid "" +"Deltas between revisions of a compressed file are usually larger than " +"snapshots of the file, and Mercurial again does <quote>the right thing</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch03-concepts.xml:561 +msgid "Network recompression" +msgstr "网络重新压缩" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:563 +msgid "" +"When storing revisions on disk, Mercurial uses the <quote>deflate</quote> " +"compression algorithm (the same one used by the popular <literal>zip</" +"literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:571 +msgid "" +"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 <literal>bzip2</literal> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:581 +msgid "" +"(If the connection is over <command>ssh</command>, Mercurial " +"<emphasis>doesn't</emphasis> recompress the stream, because <command>ssh</" +"command> can already do this itself.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:589 +msgid "Read/write ordering and atomicity" +msgstr "读写顺序与原子性" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:591 +msgid "" +"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 <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:599 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:604 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:612 +msgid "Concurrent access" +msgstr "并发访问" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:614 +msgid "" +"The read/write ordering and atomicity guarantees mean that Mercurial never " +"needs to <emphasis>lock</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:623 +msgid "" +"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 " +"<emphasis>write</emphasis> to your repository in order for them to be able to " +"clone it or pull changes from it; they only need <emphasis>read</emphasis> " +"permission. (This is <emphasis>not</emphasis> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:636 +msgid "" +"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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch03-concepts.xml:648 +msgid "Safe dirstate access" +msgstr "安全的目录状态访问" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch03-concepts.xml:650 +msgid "" +"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</filename>. The " +"file named <filename>dirstate</filename> is thus guaranteed to be complete, " +"not partially written." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:663 +msgid "Avoiding seeks" +msgstr "避免查找" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:665 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:669 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:675 +msgid "" +"Mercurial also uses a <quote>copy on write</quote> 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 <quote>hard link</quote>, " +"which is a shorthand way to say <quote>these two names point to the same " +"file</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:686 +msgid "" +"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 " +"<quote>feel</quote> of day-to-day use." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch03-concepts.xml:698 +msgid "Other contents of the dirstate" +msgstr "目录状态的其它内容" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:700 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:707 +msgid "" +"When you explicitly <command role=\"hg-cmd\">hg add</command>, <command role=" +"\"hg-cmd\">hg remove</command>, <command role=\"hg-cmd\">hg rename</command> " +"or <command role=\"hg-cmd\">hg copy</command> files, Mercurial updates the " +"dirstate so that it knows what to do with those files when you commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch03-concepts.xml:714 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch04-daily.xml:5 +msgid "Mercurial in daily use" +msgstr "Mercurial 的日常使用" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch04-daily.xml:8 +msgid "Telling Mercurial which files to track" +msgstr "告诉 Mercurial 要跟踪哪些文件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:10 +msgid "" +"Mercurial does not work with files in your repository unless you tell it to " +"manage them. The <command role=\"hg-cmd\">hg status</command> command will " +"tell you which files Mercurial doesn't know about; it uses a <quote><literal>?" +"</literal></quote> to display such files." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:17 +msgid "" +"To tell Mercurial to track a file, use the <command role=\"hg-cmd\">hg add</" +"command> command. Once you have added a file, the entry in the output of " +"<command role=\"hg-cmd\">hg status</command> for that file changes from " +"<quote><literal>?</literal></quote> to <quote><literal>A</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:26 +msgid "" +"After you run a <command role=\"hg-cmd\">hg commit</command>, the files that " +"you added before the commit will no longer be listed in the output of " +"<command role=\"hg-cmd\">hg status</command>. The reason for this is that " +"<command role=\"hg-cmd\">hg status</command> only tells you about " +"<quote>interesting</quote> files&emdash;those that you have modified or told " +"Mercurial to do something with&emdash;by default. If you have a repository " +"that contains thousands of files, you will rarely want to know about files " +"that Mercurial is tracking, but that have not changed. (You can still get " +"this information; we'll return to this later.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:38 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:45 +msgid "Explicit versus implicit file naming" +msgstr "明确与隐含文件命名" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:47 +msgid "" +"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 <quote>I " +"want to operate on every file in this directory and its subdirectories</" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:54 +msgid "" +"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</" +"filename> in the earlier example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:59 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:64 +msgid "" +"However, when we <emphasis>imply</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:73 +msgid "Aside: Mercurial tracks files, not directories" +msgstr "旁白: Mercurial 只跟踪文件,不跟踪目录" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:75 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:84 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:91 +msgid "" +"If you need an empty directory in your repository, there are a few ways to " +"achieve this. One is to create a directory, then <command role=\"hg-cmd\">hg " +"add</command> a <quote>hidden</quote> file to that directory. On Unix-like " +"systems, any file name that begins with a period (<quote><literal>.</" +"literal></quote>) is treated as hidden by most commands and GUI tools. This " +"approach is illustrated below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:102 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch04-daily.xml:109 +msgid "How to stop tracking a file" +msgstr "如何停止跟踪文件" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:111 +msgid "" +"Once you decide that a file no longer belongs in your repository, use the " +"<command role=\"hg-cmd\">hg remove</command> command; this deletes the file, " +"and tells Mercurial to stop tracking it. A removed file is represented in " +"the output of <command role=\"hg-cmd\">hg status</command> with a " +"<quote><literal>R</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:120 +msgid "" +"After you <command role=\"hg-cmd\">hg remove</command> 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 <command role=\"hg-cmd" +"\">hg add</command> it. Mercurial will know that the newly added file is not " +"related to the old file of the same name." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:129 +msgid "Removing a file does not affect its history" +msgstr "删除文件不影响历史" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:131 +msgid "It is important to understand that removing a file has only two effects." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch04-daily.xml:134 +msgid "It removes the current version of the file from the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch04-daily.xml:137 +msgid "" +"It stops Mercurial from tracking changes to the file, from the time of the " +"next commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:140 +msgid "" +"Removing a file <emphasis>does not</emphasis> in any way alter the " +"<emphasis>history</emphasis> of the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:143 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:153 +msgid "Missing files" +msgstr "丢失的文件" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:155 +msgid "" +"Mercurial considers a file that you have deleted, but not used <command role=" +"\"hg-cmd\">hg remove</command> to delete, to be <emphasis>missing</" +"emphasis>. A missing file is represented with <quote><literal>!</literal></" +"quote> in the output of <command role=\"hg-cmd\">hg status</command>. " +"Mercurial commands will not generally do anything with missing files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:165 +msgid "" +"If your repository contains a file that <command role=\"hg-cmd\">hg status</" +"command> reports as missing, and you want the file to stay gone, you can run " +"<command role=\"hg-cmd\">hg remove <option role=\"hg-opt-remove\">--after</" +"option></command> at any time later on, to tell Mercurial that you really did " +"mean to remove the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:175 +msgid "" +"On the other hand, if you deleted the missing file by accident, give <command " +"role=\"hg-cmd\">hg revert</command> the name of the file to recover. It will " +"reappear, in unmodified form." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:184 +msgid "Aside: why tell Mercurial explicitly to remove a file?" +msgstr "旁白: 为什么要明确告诉 Mercurial 删除文件?" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:187 +msgid "" +"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 <command role=\"hg-cmd\">hg commit</" +"command>, and stop tracking the file. In practice, this made it too easy to " +"accidentally remove a file without noticing." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:198 +msgid "Useful shorthand&emdash;adding and removing files in one step" +msgstr "有用的速记—一个步骤添加和删除文件" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:201 +msgid "" +"Mercurial offers a combination command, <command role=\"hg-cmd\">hg " +"addremove</command>, that adds untracked files and marks missing files as " +"removed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:207 +msgid "" +"The <command role=\"hg-cmd\">hg commit</command> command also provides a " +"<option role=\"hg-opt-commit\">-A</option> option that performs this same add-" +"and-remove, immediately followed by a commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch04-daily.xml:217 +msgid "Copying files" +msgstr "复制文件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:219 +msgid "" +"Mercurial provides a <command role=\"hg-cmd\">hg copy</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:227 +msgid "The results of copying during a merge" +msgstr "合并期间的复制结果" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:229 +msgid "" +"What happens during a merge is that changes <quote>follow</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:236 +msgid "" +"We need to do some work in parallel, so that we'll have something to merge. " +"So let's clone our repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:242 +msgid "" +"Back in our initial repository, let's use the <command role=\"hg-cmd\">hg " +"copy</command> command to make a copy of the first file we created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:248 +msgid "" +"If we look at the output of the <command role=\"hg-cmd\">hg status</command> " +"command afterwards, the copied file looks just like a normal added file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:254 +msgid "" +"But if we pass the <option role=\"hg-opt-status\">-C</option> option to " +"<command role=\"hg-cmd\">hg status</command>, it prints another line of " +"output: this is the file that our newly-added file was copied <emphasis>from</" +"emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:262 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:268 +msgid "" +"Now we have a modified <filename>file</filename> 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</" +"filename> into its copy, <filename>new-file</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:278 +msgid "Why should changes follow copies?" +msgstr "为什么复制后需要后续修改?" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:280 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:284 +msgid "" +"First of all, remember that this propagation <emphasis>only</emphasis> " +"happens when you merge. So if you <command role=\"hg-cmd\">hg copy</command> " +"a file, and subsequently modify the original file during the normal course of " +"your work, nothing will happen." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:290 +msgid "" +"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 " +"<emphasis>doesn't know</emphasis> about the copy." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:295 +msgid "" +"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 <command role=\"hg-cmd\">hg copy</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:302 +msgid "" +"If you pulled and merged my changes, and Mercurial <emphasis>didn't</" +"emphasis> 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 <emphasis>remain</emphasis> in your copy of the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:308 +msgid "" +"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 <emphasis>only</emphasis> revision control system that " +"propagates changes across copies like this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:314 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:322 +msgid "How to make changes <emphasis>not</emphasis> follow a copy" +msgstr "如何让复制后<emphasis>不</emphasis>修改?" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:325 +msgid "" +"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</command>) " +"to make a copy of a file, then <command role=\"hg-cmd\">hg add</command> the " +"new copy by hand. Before you do so, though, please do reread <xref linkend=" +"\"sec:daily:why-copy\"/>, and make an informed decision that this behaviour " +"is not appropriate to your specific case." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:338 +msgid "Behaviour of the <command role=\"hg-cmd\">hg copy</command> command" +msgstr "命令 <command role=\"hg-cmd\">hg copy</command> 的特性" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:341 +msgid "" +"When you use the <command role=\"hg-cmd\">hg copy</command> 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 <command role=\"hg-cmd\">hg copy</command> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:351 +msgid "" +"The <command role=\"hg-cmd\">hg copy</command> command acts similarly to the " +"Unix <command>cp</command> command (you can use the <command role=\"hg-cmd" +"\">hg cp</command> alias if you prefer). The last argument is the " +"<emphasis>destination</emphasis>, and all prior arguments are " +"<emphasis>sources</emphasis>. If you pass it a single file as the source, " +"and the destination does not exist, it creates a new file with that name." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:362 +msgid "" +"If the destination is a directory, Mercurial copies its sources into that " +"directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:367 +msgid "" +"Copying a directory is recursive, and preserves the directory structure of " +"the source." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:373 +msgid "" +"If the source and destination are both directories, the source tree is " +"recreated in the destination directory." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:378 +msgid "" +"As with the <command role=\"hg-cmd\">hg rename</command> command, if you copy " +"a file manually and then want Mercurial to know that you've copied the file, " +"simply use the <option role=\"hg-opt-copy\">--after</option> option to " +"<command role=\"hg-cmd\">hg copy</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch04-daily.xml:389 +msgid "Renaming files" +msgstr "改名文件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:391 +msgid "" +"It's rather more common to need to rename a file than to make a copy of it. " +"The reason I discussed the <command role=\"hg-cmd\">hg copy</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:399 +msgid "" +"When you use the <command role=\"hg-cmd\">hg rename</command> command, " +"Mercurial makes a copy of each source file, then deletes it and marks the " +"file as removed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:405 +msgid "" +"The <command role=\"hg-cmd\">hg status</command> command shows the newly " +"copied file as added, and the copied-from file as removed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:411 +msgid "" +"As with the results of a <command role=\"hg-cmd\">hg copy</command>, we must " +"use the <option role=\"hg-opt-status\">-C</option> option to <command role=" +"\"hg-cmd\">hg status</command> to see that the added file is really being " +"tracked by Mercurial as a copy of the original, now removed, file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:420 +msgid "" +"As with <command role=\"hg-cmd\">hg remove</command> and <command role=\"hg-" +"cmd\">hg copy</command>, you can tell Mercurial about a rename after the fact " +"using the <option role=\"hg-opt-rename\">--after</option> option. In most " +"other respects, the behaviour of the <command role=\"hg-cmd\">hg rename</" +"command> command, and the options it accepts, are similar to the <command " +"role=\"hg-cmd\">hg copy</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:430 +msgid "Renaming files and merging changes" +msgstr "改名文件与合并修改" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:432 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:436 +msgid "" +"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 <quote>simply work,</quote> but not all revision control systems " +"actually do this.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:443 +msgid "" +"Whereas having changes follow a copy is a feature where you can perhaps nod " +"and say <quote>yes, that might be useful,</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:452 +msgid "Divergent renames and merging" +msgstr "改名与合并的分歧" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:454 +msgid "" +"The case of diverging names occurs when two developers start with a " +"file&emdash;let's call it <filename>foo</filename>&emdash;in their respective " +"repositories." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:461 +msgid "Anne renames the file to <filename>bar</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:465 +msgid "Meanwhile, Bob renames it to <filename>quux</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:470 +msgid "" +"I like to think of this as a conflict because each developer has expressed " +"different intentions about what the file ought to be named." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:474 +msgid "" +"What do you think should happen when they merge their work? Mercurial's " +"actual behaviour is that it always preserves <emphasis>both</emphasis> names " +"when it merges changesets that contain divergent renames." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:481 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:487 +msgid "Convergent renames and merging" +msgstr "收敛改名与合并" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:489 +msgid "" +"Another kind of rename conflict occurs when two people choose to rename " +"different <emphasis>source</emphasis> files to the same " +"<emphasis>destination</emphasis>. In this case, Mercurial runs its normal " +"merge machinery, and lets you guide it to a suitable resolution." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch04-daily.xml:497 +msgid "Other name-related corner cases" +msgstr "其它名称相关的角落" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch04-daily.xml:499 +msgid "" +"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 <ulink role=\"hg-bug\" url=\"http://www." +"selenic.com/mercurial/bts/issue29\">issue 29</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch04-daily.xml:511 +msgid "Recovering from mistakes" +msgstr "从错误恢复" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:513 +msgid "" +"Mercurial has some useful commands that will help you to recover from some " +"common mistakes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:516 +msgid "" +"The <command role=\"hg-cmd\">hg revert</command> command lets you undo " +"changes that you have made to your working directory. For example, if you " +"<command role=\"hg-cmd\">hg add</command> a file by accident, just run " +"<command role=\"hg-cmd\">hg revert</command> 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 <command role=" +"\"hg-cmd\">hg revert</command> to get rid of erroneous changes to a file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:526 +msgid "" +"It's useful to remember that the <command role=\"hg-cmd\">hg revert</command> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch04-daily.xml:532 +msgid "" +"For more information about the <command role=\"hg-cmd\">hg revert</command> " +"command, and details about how to deal with changes you have already " +"committed, see <xref linkend=\"chap:undo\"/>." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch05-collab.xml:5 +msgid "Collaborating with other people" +msgstr "团体协作" + +#. type: Content of: <book><chapter><para> +#: ../en/ch05-collab.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:14 +msgid "Mercurial's web interface" +msgstr "Mercurial 的 web 接口" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:16 +msgid "" +"Mercurial has a powerful web interface that provides several useful " +"capabilities." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:19 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:24 +msgid "" +"Also for human consumption, the web interface provides an RSS feed of the " +"changes in a repository. This lets you <quote>subscribe</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:34 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:40 +msgid "" +"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 <ulink url=\"http://www.selenic.com/repo/hg?style=gitweb" +"\">http://www.selenic.com/repo/hg?style=gitweb</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:45 +msgid "" +"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 <command role=" +"\"hg-cmd\">hg serve</command> command, which is best suited to short-term " +"<quote>lightweight</quote> serving. See <xref linkend=\"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 <xref linkend=\"sec:collab:cgi\"/> for details of " +"CGI configuration." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:60 +msgid "Collaboration models" +msgstr "协作模型" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:62 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:70 +msgid "Factors to keep in mind" +msgstr "要牢记的因素" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:72 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:78 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:88 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:102 +msgid "Informal anarchy" +msgstr "无政府状态" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:104 +msgid "" +"I wouldn't suggest an <quote>anything goes</quote> approach as something " +"sustainable, but it's a model that's easy to grasp, and it works perfectly " +"well in a few unusual situations." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:109 +msgid "" +"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 <quote>sprints</" +"quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:118 +msgid "" +"A sprint is the perfect place to use the <command role=\"hg-cmd\">hg serve</" +"command> command, since <command role=\"hg-cmd\">hg serve</command> does not " +"require any fancy server infrastructure. You can get started with <command " +"role=\"hg-cmd\">hg serve</command> in moments, by reading <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:132 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:141 +msgid "A single central repository" +msgstr "单一中央版本库" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:143 +msgid "" +"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 " +"<quote>building block</quote> for more ambitious workflow schemes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:149 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:154 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:165 +msgid "" +"In this kind of scenario, people usually use the <command>ssh</command> " +"protocol to securely push changes to the central repository, as documented in " +"<xref linkend=\"sec:collab:ssh\"/>. It's also usual to publish a read-only " +"copy of the repository over HTTP using CGI, as in <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:177 +msgid "Working with multiple branches" +msgstr "使用多个分支工作" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:179 +msgid "" +"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 " +"<quote>maintenance mode</quote> 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 <quote>branch</quote> to " +"refer to one of these many slightly different directions in which development " +"is proceeding." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:192 +msgid "" +"Mercurial is particularly well suited to managing a number of simultaneous, " +"but not identical, branches. Each <quote>development direction</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:201 +msgid "" +"Here's an example of how this can work in practice. Let's say you have one " +"<quote>main branch</quote> on a central server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:207 +msgid "People clone it, make changes locally, test them, and push them back." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:210 +msgid "" +"Once the main branch reaches a release milestone, you can use the <command " +"role=\"hg-cmd\">hg tag</command> command to give a permanent name to the " +"milestone revision." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:216 +msgid "Let's say some ongoing development occurs on the main branch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:221 +msgid "" +"Using the tag that was recorded at the milestone, people who clone that " +"repository at any time in the future can use <command role=\"hg-cmd\">hg " +"update</command> to get a copy of the working directory exactly as it was " +"when that tagged revision was committed." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:229 +msgid "" +"In addition, immediately after the main branch is tagged, someone can then " +"clone the main branch on the server to a new <quote>stable</quote> branch, " +"also on the server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:235 +msgid "" +"Someone who needs to make a change to the stable branch can then clone " +"<emphasis>that</emphasis> repository, make their changes, commit, and push " +"their changes back there." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:241 +msgid "" +"Because Mercurial repositories are independent, and Mercurial doesn't move " +"changes around automatically, the stable and main branches are " +"<emphasis>isolated</emphasis> from each other. The changes that you made on " +"the main branch don't <quote>leak</quote> to the stable branch, and vice " +"versa." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:248 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:256 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch05-collab.xml:263 ../en/ch05-collab.xml:273 +msgid "Feature branches" +msgstr "特性分支" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:265 +msgid "" +"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 <quote>master</quote> branch used by the entire project. People " +"working on an individual branch are typically quite isolated from " +"developments on other branches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch05-collab.xml:275 +msgid "" +"<imageobject><imagedata width=\"100%\" fileref=\"figs/feature-branches.png\"/" +"></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:280 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:287 +msgid "The release train" +msgstr "发布列车" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:289 +msgid "" +"Some projects are organised on a <quote>train</quote> basis: a release is " +"scheduled to happen every few months, and whatever features are ready when " +"the <quote>train</quote> is ready to leave are allowed in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:294 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:303 +msgid "The Linux kernel model" +msgstr "Linux 内核模型" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:305 +msgid "" +"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</command>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:313 +msgid "" +"At the center of the community sits Linus Torvalds, the creator of Linux. He " +"publishes a single source repository that is considered the " +"<quote>authoritative</quote> current tree by the entire developer community. " +"Anyone can clone Linus's tree, but he is very choosy about whose trees he " +"pulls from." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:320 +msgid "" +"Linus has a number of <quote>trusted lieutenants</quote>. 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 " +"<quote>maintainers</quote>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:332 +msgid "" +"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 <quote>stable</quote> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:343 +msgid "" +"This model has two notable features. The first is that it's <quote>pull " +"only</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:350 +msgid "" +"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 " +"<quote>good</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:361 +msgid "" +"Reputation and acclaim don't necessarily cross subsystem or <quote>people</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:368 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:378 +msgid "Pull-only versus shared-push collaboration" +msgstr "只读与共享写协作" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:380 +msgid "" +"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 " +"<quote>better than</quote> one in which multiple people can push changes to a " +"shared repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:386 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:394 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:402 +msgid "Where collaboration meets branch management" +msgstr "协作与分支管理" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:404 +msgid "" +"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 <xref linkend=\"chap:branch\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:416 +msgid "The technical side of sharing" +msgstr "共享的技术因素" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:418 +msgid "" +"The remainder of this chapter is devoted to the question of serving data to " +"your collaborators." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:423 +msgid "Informal sharing with <command role=\"hg-cmd\">hg serve</command>" +msgstr "使用 <command role=\"hg-cmd\">hg serve</command> 进行非正式共享" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:426 +msgid "" +"Mercurial's <command role=\"hg-cmd\">hg serve</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:431 +msgid "" +"Run <command role=\"hg-cmd\">hg serve</command> 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 <command role=\"hg-" +"cmd\">hg serve</command> instance running on a laptop is likely to look " +"something like <literal>http://my-laptop.local:8000/</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:442 +msgid "" +"The <command role=\"hg-cmd\">hg serve</command> command is <emphasis>not</" +"emphasis> a general-purpose web server. It can do only two things:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:446 +msgid "" +"Allow people to browse the history of the repository it's serving, from their " +"normal web browsers." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:450 +msgid "" +"Speak Mercurial's wire protocol, so that people can <command role=\"hg-cmd" +"\">hg clone</command> or <command role=\"hg-cmd\">hg pull</command> changes " +"from that repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:455 +msgid "" +"In particular, <command role=\"hg-cmd\">hg serve</command> won't allow remote " +"users to <emphasis>modify</emphasis> your repository. It's intended for read-" +"only use." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:459 +msgid "" +"If you're getting started with Mercurial, there's nothing to prevent you from " +"using <command role=\"hg-cmd\">hg serve</command> to serve up a repository on " +"your own computer, then use commands like <command role=\"hg-cmd\">hg clone</" +"command>, <command role=\"hg-cmd\">hg incoming</command>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:469 +msgid "A few things to keep in mind" +msgstr "要牢记的几件事" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:471 +msgid "" +"Because it provides unauthenticated read access to all clients, you should " +"only use <command role=\"hg-cmd\">hg serve</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:477 +msgid "" +"The <command role=\"hg-cmd\">hg serve</command> 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 <command role=\"hg-cmd\">hg serve</command> instance, " +"the second thing you should do (<emphasis>after</emphasis> you make sure that " +"they're using the correct URL) is check your firewall configuration." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:486 +msgid "" +"By default, <command role=\"hg-cmd\">hg serve</command> listens for incoming " +"connections on port 8000. If another process is already listening on the " +"port you want to use, you can specify a different port to listen on using the " +"<option role=\"hg-opt-serve\">-p</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:492 +msgid "" +"Normally, when <command role=\"hg-cmd\">hg serve</command> starts, it prints " +"no output, which can be a bit unnerving. If you'd like to confirm that it is " +"indeed running correctly, and find out what URL you should send to your " +"collaborators, start it with the <option role=\"hg-opt-global\">-v</option> " +"option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:502 +msgid "Using the Secure Shell (ssh) protocol" +msgstr "使用 ssh 协议" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:504 +msgid "" +"You can pull and push changes securely over a network connection using the " +"Secure Shell (<literal>ssh</literal>) protocol. To use this successfully, " +"you may have to do a little bit of configuration on the client or server " +"sides." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:509 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:515 +msgid "" +"(If you <emphasis>are</emphasis> familiar with ssh, you'll probably find some " +"of the material that follows to be elementary in nature.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:520 +msgid "How to read and write ssh URLs" +msgstr "如何读写 ssh 路径" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:522 +msgid "An ssh URL tends to look like this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:525 +msgid "" +"The <quote><literal>ssh://</literal></quote> part tells Mercurial to use the " +"ssh protocol." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:528 +msgid "" +"The <quote><literal>bos@</literal></quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:533 +msgid "" +"The <quote><literal>hg.serpentine.com</literal></quote> gives the hostname of " +"the server to log into." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:537 +msgid "" +"The <quote>:22</quote> identifies the port number to connect to the server " +"on. The default port is 22, so you only need to specify a colon and port " +"number if you're <emphasis>not</emphasis> using port 22." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:542 +msgid "" +"The remainder of the URL is the local path to the repository on the server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:546 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:553 +msgid "" +"Mercurial treats the path to a repository on the server as relative to the " +"remote user's home directory. For example, if user <literal>foo</literal> on " +"the server has a home directory of <filename class=\"directory\">/home/foo</" +"filename>, then an ssh URL that contains a path component of <filename class=" +"\"directory\">bar</filename> <emphasis>really</emphasis> refers to the " +"directory <filename class=\"directory\">/home/foo/bar</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:562 +msgid "" +"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 <literal>otheruser</literal>), like this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:568 +msgid "" +"And if you really want to specify an <emphasis>absolute</emphasis> path on " +"the server, begin the path component with two slashes, as in this example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:575 +msgid "Finding an ssh client for your system" +msgstr "为你的系统寻找 ssh 客户端" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:577 +msgid "" +"Almost every Unix-like system comes with OpenSSH preinstalled. If you're " +"using such a system, run <literal>which ssh</literal> to find out if the " +"<command>ssh</command> command is installed (it's usually in <filename class=" +"\"directory\">/usr/bin</filename>). In the unlikely event that it isn't " +"present, take a look at your system documentation to figure out how to " +"install it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:585 +msgid "" +"On Windows, you'll first need to download a suitable ssh client. There are " +"two alternatives." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:588 +msgid "" +"Simon Tatham's excellent PuTTY package <citation>web:putty</citation> " +"provides a complete suite of ssh client commands." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:592 +msgid "" +"If you have a high tolerance for pain, you can use the Cygwin port of OpenSSH." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:595 +msgid "" +"In either case, you'll need to edit your <filename role=\"special\">hg.ini</" +"filename> 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> command as a command-line ssh client." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch05-collab.xml:605 +msgid "" +"The path to <command>plink</command> shouldn't contain any whitespace " +"characters, or Mercurial may not be able to run it correctly (so putting it " +"in <filename class=\"directory\">C:\\Program Files</filename> is probably not " +"a good idea)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:614 +msgid "Generating a key pair" +msgstr "产生密钥对" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:616 +msgid "" +"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> command will do the trick. On Windows, if " +"you're using PuTTY, the <command>puttygen</command> command is what you'll " +"need." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:624 +msgid "" +"When you generate a key pair, it's usually <emphasis>highly</emphasis> " +"advisable to protect it with a passphrase. (The only time that you might not " +"want to do this is when you're using the ssh protocol for automated tasks on " +"a secure network.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:630 +msgid "" +"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 <filename role=\"special" +"\">authorized_keys</filename> in their <filename role=\"special\" class=" +"\"directory\">.ssh</filename> directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:639 +msgid "" +"On a Unix-like system, your public key will have a <filename>.pub</filename> " +"extension. If you're using <command>puttygen</command> 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 <filename role=\"special" +"\">authorized_keys</filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:648 +msgid "Using an authentication agent" +msgstr "使用认证代理" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:650 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:659 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:666 +msgid "" +"On Unix-like systems, the agent is called <command>ssh-agent</command>, and " +"it's often run automatically for you when you log in. You'll need to use the " +"<command>ssh-add</command> command to add passphrases to the agent's store. " +"On Windows, if you're using PuTTY, the <command>pageant</command> command " +"acts as the agent. It adds an icon to your system tray that will let you " +"manage stored passphrases." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:677 +msgid "Configuring the server side properly" +msgstr "正确配置服务器端" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:679 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:687 +msgid "" +"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</command> or <command>putty</" +"command> 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, <emphasis>before</emphasis> you worry about whether " +"there's a problem with Mercurial." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:698 +msgid "" +"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</command> or " +"<command>putty</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:705 +msgid "" +"If you get a <quote>connection refused</quote> error, either there isn't an " +"SSH daemon running on the server at all, or it's inaccessible due to firewall " +"configuration." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:710 +msgid "" +"If you get a <quote>no route to host</quote> error, you either have an " +"incorrect address for the server or a seriously locked down firewall that " +"won't admit its existence at all." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:715 +msgid "" +"If you get a <quote>permission denied</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:720 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:730 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:736 +msgid "" +"You might have forgotten to use <command>ssh-add</command> or " +"<command>pageant</command> to store the passphrase." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:740 +msgid "You might have stored the passphrase for the wrong key." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:743 +msgid "" +"If you're being prompted for the remote user's password, there are another " +"few possible problems to check." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:746 +msgid "" +"Either the user's home directory or their <filename role=\"special\" class=" +"\"directory\">.ssh</filename> directory might have excessively liberal " +"permissions. As a result, the ssh daemon will not trust or read their " +"<filename role=\"special\">authorized_keys</filename> file. For example, a " +"group-writable home or <filename role=\"special\" class=\"directory\">.ssh</" +"filename> directory will often cause this symptom." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:755 +msgid "" +"The user's <filename role=\"special\">authorized_keys</filename> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:762 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:767 +msgid "" +"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</command> 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 <literal>tty -s</literal>.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:781 +msgid "" +"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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:788 +msgid "" +"If you see an error message instead of normal <command role=\"hg-cmd\">hg " +"version</command> output, this is usually because you haven't installed " +"Mercurial to <filename class=\"directory\">/usr/bin</filename>. Don't worry " +"if this is the case; you don't need to do that. But you should check for a " +"few possible problems." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:795 +msgid "" +"Is Mercurial really installed on the server at all? I know this sounds " +"trivial, but it's worth checking!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:799 +msgid "" +"Maybe your shell's search path (usually set via the <envar>PATH</envar> " +"environment variable) is simply misconfigured." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:803 +msgid "" +"Perhaps your <envar>PATH</envar> environment variable is only being set to " +"point to the location of the <command>hg</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:810 +msgid "" +"The <envar>PYTHONPATH</envar> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:817 +msgid "" +"If you can run <command role=\"hg-cmd\">hg version</command> over an ssh " +"connection, well done! You've got the server and client sorted out. You " +"should now be able to use Mercurial to access repositories hosted by that " +"username on that server. If you run into problems with Mercurial and ssh at " +"this point, try using the <option role=\"hg-opt-global\">--debug</option> " +"option to get a clearer picture of what's going on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:827 +msgid "Using compression with ssh" +msgstr "通过 ssh 使用压缩" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:829 +msgid "" +"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 <emphasis>not</emphasis> to request compression." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:834 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:841 +msgid "" +"Both <command>ssh</command> and <command>plink</command> accept a <option " +"role=\"cmd-opt-ssh\">-C</option> option which turns on compression. You can " +"easily edit your <filename role=\"special\">~/.hgrc</filename> to enable " +"compression for all of Mercurial's uses of the ssh protocol." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:849 +msgid "" +"If you use <command>ssh</command>, you can configure it to always use " +"compression when talking to your server. To do this, edit your <filename " +"role=\"special\">.ssh/config</filename> file (which may not yet exist), as " +"follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:857 +msgid "" +"This defines an alias, <literal>hg</literal>. When you use it on the " +"<command>ssh</command> command line or in a Mercurial <literal>ssh</literal>-" +"protocol URL, it will cause <command>ssh</command> to connect to <literal>hg." +"example.com</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch05-collab.xml:868 +msgid "Serving over HTTP using CGI" +msgstr "使用 CGI 通过 HTTP 提供服务" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:870 +msgid "" +"Depending on how ambitious you are, configuring Mercurial's CGI interface can " +"take anything from a few moments to several hours." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch05-collab.xml:874 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch05-collab.xml:880 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:890 +msgid "Web server configuration checklist" +msgstr "Web 服务器配置检查表" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:892 +msgid "" +"Before you continue, do take a few moments to check a few aspects of your " +"system's setup." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:896 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:900 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch05-collab.xml:904 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:910 +msgid "" +"If you don't have a web server installed, and don't have substantial " +"experience configuring Apache, you should consider using the " +"<literal>lighttpd</literal> web server instead of Apache. Apache has a well-" +"deserved reputation for baroque and confusing configuration. While " +"<literal>lighttpd</literal> is less capable in some ways than Apache, most of " +"these capabilities are not relevant to serving Mercurial repositories. And " +"<literal>lighttpd</literal> is undeniably <emphasis>much</emphasis> easier to " +"get started with than Apache." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:923 +msgid "Basic CGI configuration" +msgstr "基本 CGI 配置" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:925 +msgid "" +"On Unix-like systems, it's common for users to have a subdirectory named " +"something like <filename class=\"directory\">public_html</filename> in their " +"home directory, from which they can serve up web pages. A file named " +"<filename>foo</filename> in this directory will be accessible at a URL of the " +"form <literal>http://www.example.com/username/foo</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:933 +msgid "" +"To get started, find the <filename role=\"special\">hgweb.cgi</filename> " +"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 <ulink url=\"http://www.selenic.com/repo/hg/raw-file/" +"tip/hgweb.cgi\">http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:940 ../en/ch05-collab.xml:1109 +msgid "" +"You'll need to copy this script into your <filename class=\"directory" +"\">public_html</filename> directory, and ensure that it's executable." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:945 +msgid "" +"The <literal>755</literal> argument to <command>chmod</command> is a little " +"more general than just making the script executable: it ensures that the " +"script is executable by anyone, and that <quote>group</quote> and " +"<quote>other</quote> write permissions are <emphasis>not</emphasis> set. If " +"you were to leave those write permissions enabled, Apache's <literal>suexec</" +"literal> subsystem would likely refuse to execute the script. In fact, " +"<literal>suexec</literal> also insists that the <emphasis>directory</" +"emphasis> in which the script resides must not be writable by others." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:959 +msgid "What could <emphasis>possibly</emphasis> go wrong?" +msgstr "什么<emphasis>可能</emphasis>会出错?" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:962 +msgid "" +"Once you've copied the CGI script into place, go into a web browser, and try " +"to open the URL <ulink url=\"http://myhostname/ myuser/hgweb.cgi\">http://" +"myhostname/ myuser/hgweb.cgi</ulink>, <emphasis>but</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:976 +msgid "" +"Your web server may have per-user directories disabled. If you're using " +"Apache, search your config file for a <literal>UserDir</literal> directive. " +"If there's none present, per-user directories will be disabled. If one " +"exists, but its value is <literal>disabled</literal>, then per-user " +"directories will be disabled. Otherwise, the string after <literal>UserDir</" +"literal> gives the name of the subdirectory that Apache will look in under " +"your home directory, for example <filename class=\"directory\">public_html</" +"filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:987 +msgid "" +"Your file access permissions may be too restrictive. The web server must be " +"able to traverse your home directory and directories under your <filename " +"class=\"directory\">public_html</filename> directory, and read files under " +"the latter too. Here's a quick recipe to help you to make your permissions " +"more appropriate." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:997 +msgid "" +"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 <emphasis>too permissive</emphasis>. Apache's " +"<literal>suexec</literal> subsystem won't execute a script that's group- or " +"world-writable, for example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1004 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1011 +msgid "" +"If you find a similar-looking <literal>Directory</literal> group in your " +"Apache configuration, the directive to look at inside it is <literal>Options</" +"literal>. Add <literal>ExecCGI</literal> to the end of this list if it's " +"missing, and restart the web server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1018 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1024 +msgid "" +"The next possibility is that you might be served with a colourful Python " +"backtrace claiming that it can't import a <literal>mercurial</literal>-" +"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 <filename role=" +"\"special\">hgweb.cgi</filename> and follow the directions inside it to " +"correctly set your <envar>PYTHONPATH</envar> environment variable." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1038 +msgid "" +"Finally, you are <emphasis>certain</emphasis> to by served with another " +"colourful Python backtrace: this one will complain that it can't find " +"<filename class=\"directory\">/path/to/repository</filename>. Edit your " +"<filename role=\"special\">hgweb.cgi</filename> script and replace the " +"<filename class=\"directory\">/path/to/repository</filename> string with the " +"complete path to the repository you want to serve up." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1048 +msgid "" +"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!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:1054 +msgid "Configuring lighttpd" +msgstr "配置 lighttpd" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1056 +msgid "" +"To be exhaustive in my experiments, I tried configuring the increasingly " +"popular <literal>lighttpd</literal> 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 <filename role=\"special\">hgweb.cgi</filename> script was properly edited." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1066 +msgid "" +"Once I had Apache running, getting <literal>lighttpd</literal> to serve the " +"repository was a snap (in other words, even if you're trying to use " +"<literal>lighttpd</literal>, you should read the Apache section). I first " +"had to edit the <literal>mod_access</literal> section of its config file to " +"enable <literal>mod_cgi</literal> and <literal>mod_userdir</literal>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1078 +msgid "" +"With this done, <literal>lighttpd</literal> ran immediately for me. If I had " +"configured <literal>lighttpd</literal> 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 <literal>lighttpd</literal> 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 <literal>lighttpd</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:1091 +msgid "Sharing multiple repositories with one CGI script" +msgstr "使用一个 CGI 脚本共享多个版本库" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1093 +msgid "" +"The <filename role=\"special\">hgweb.cgi</filename> 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 " +"<filename role=\"special\">hgwebdir.cgi</filename> script." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1101 +msgid "" +"The procedure to configure <filename role=\"special\">hgwebdir.cgi</filename> " +"is only a little more involved than for <filename role=\"special\">hgweb.cgi</" +"filename>. 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 " +"<ulink url=\"http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi" +"\">http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1116 +msgid "" +"With basic configuration out of the way, try to visit <ulink url=\"http://" +"myhostname/ myuser/hgwebdir.cgi\">http://myhostname/ myuser/hgwebdir.cgi</" +"ulink> 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 <xref linkend=\"sec:collab:wtf\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1125 +msgid "" +"The <filename role=\"special\">hgwebdir.cgi</filename> script relies on an " +"external configuration file. By default, it searches for a file named " +"<filename role=\"special\">hgweb.config</filename> 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 <quote>ini</quote> file, as " +"understood by Python's <literal>ConfigParser</literal> <citation>web:" +"configparser</citation> module." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1135 +msgid "" +"The easiest way to configure <filename role=\"special\">hgwebdir.cgi</" +"filename> is with a section named <literal>collections</literal>. This will " +"automatically publish <emphasis>every</emphasis> repository under the " +"directories you name. The section should look like this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1143 +msgid "" +"Mercurial interprets this by looking at the directory name on the " +"<emphasis>right</emphasis> hand side of the <quote><literal>=</literal></" +"quote> sign; finding repositories in that directory hierarchy; and using the " +"text on the <emphasis>left</emphasis> 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 <quote>virtual path</" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1152 +msgid "" +"Given the example above, if we have a repository whose local path is " +"<filename class=\"directory\">/my/root/this/repo</filename>, the CGI script " +"will strip the leading <filename class=\"directory\">/my/root</filename> from " +"the name, and publish the repository with a virtual path of <filename class=" +"\"directory\">this/repo</filename>. If the base URL for our CGI script is " +"<ulink url=\"http://myhostname/ myuser/hgwebdir.cgi\">http://myhostname/ " +"myuser/hgwebdir.cgi</ulink>, the complete URL for that repository will be " +"<ulink url=\"http://myhostname/ myuser/hgwebdir.cgi/this/repo\">http://" +"myhostname/ myuser/hgwebdir.cgi/this/repo</ulink>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1166 +msgid "" +"If we replace <filename class=\"directory\">/my/root</filename> on the left " +"hand side of this example with <filename class=\"directory\">/my</filename>, " +"then <filename role=\"special\">hgwebdir.cgi</filename> will only strip off " +"<filename class=\"directory\">/my</filename> from the repository name, and " +"will give us a virtual path of <filename class=\"directory\">root/this/repo</" +"filename> instead of <filename class=\"directory\">this/repo</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1176 +msgid "" +"The <filename role=\"special\">hgwebdir.cgi</filename> script will " +"recursively search each directory listed in the <literal>collections</" +"literal> section of its configuration file, but it will <literal>not</" +"literal> recurse into the repositories it finds." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1182 +msgid "" +"The <literal>collections</literal> mechanism makes it easy to publish many " +"repositories in a <quote>fire and forget</quote> 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 <filename role=" +"\"special\">hgwebdir.cgi</filename> to look." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:1192 +msgid "Explicitly specifying which repositories to publish" +msgstr "明确指出要发布的版本库" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1195 +msgid "" +"In addition to the <literal>collections</literal> mechanism, the <filename " +"role=\"special\">hgwebdir.cgi</filename> script allows you to publish a " +"specific list of repositories. To do so, create a <literal>paths</literal> " +"section, with contents of the following form." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1204 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1211 +msgid "" +"If you wish, you can use both the <literal>collections</literal> and " +"<literal>paths</literal> mechanisms simultaneously in a single configuration " +"file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><note><para> +#: ../en/ch05-collab.xml:1217 +msgid "" +"If multiple repositories have the same virtual path, <filename role=\"special" +"\">hgwebdir.cgi</filename> will not report an error. Instead, it will behave " +"unpredictably." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:1226 +msgid "Downloading source archives" +msgstr "下载源代码档案包" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1228 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1233 +msgid "" +"By default, this feature is not enabled. To enable it, you'll need to add an " +"<envar role=\"rc-item-web\">allow_archive</envar> item to the <literal role=" +"\"rc-web\">web</literal> section of your <filename role=\"special\">~/.hgrc</" +"filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:1241 +msgid "Web configuration options" +msgstr "Web 配置选项" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1243 +msgid "" +"Mercurial's web interfaces (the <command role=\"hg-cmd\">hg serve</command> " +"command, and the <filename role=\"special\">hgweb.cgi</filename> and " +"<filename role=\"special\">hgwebdir.cgi</filename> scripts) have a number of " +"configuration options that you can set. These belong in a section named " +"<literal role=\"rc-web\">web</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1251 +msgid "" +"<envar role=\"rc-item-web\">allow_archive</envar>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1260 +msgid "" +"<literal>bz2</literal>: A <command>tar</command> archive, compressed using " +"<literal>bzip2</literal> compression. This has the best compression ratio, " +"but uses the most CPU time on the server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1266 +msgid "" +"<literal>gz</literal>: A <command>tar</command> archive, compressed using " +"<literal>gzip</literal> compression." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1270 +msgid "" +"<literal>zip</literal>: A <command>zip</command> archive, compressed using " +"LZW compression. This format has the worst compression ratio, but is widely " +"used in the Windows world." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1276 +msgid "" +"If you provide an empty list, or don't have an <envar role=\"rc-item-web" +"\">allow_archive</envar> entry at all, this feature will be disabled. Here " +"is an example of how to enable all three supported formats." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1283 +msgid "" +"<envar role=\"rc-item-web\">allowpull</envar>: Boolean. Determines whether " +"the web interface allows remote users to <command role=\"hg-cmd\">hg pull</" +"command> and <command role=\"hg-cmd\">hg clone</command> this repository over " +"HTTP. If set to <literal>no</literal> or <literal>false</literal>, only the " +"<quote>human-oriented</quote> portion of the web interface is available." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1292 +msgid "" +"<envar role=\"rc-item-web\">contact</envar>: 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 " +"<filename role=\"special\">.hg/hgrc</filename> file, but it can make sense to " +"use in a global <filename role=\"special\">~/.hgrc</filename> if every " +"repository has a single maintainer." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1303 +msgid "" +"<envar role=\"rc-item-web\">maxchanges</envar>: Integer. The default maximum " +"number of changesets to display in a single page of output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1307 +msgid "" +"<envar role=\"rc-item-web\">maxfiles</envar>: Integer. The default maximum " +"number of modified files to display in a single page of output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1311 +msgid "" +"<envar role=\"rc-item-web\">stripes</envar>: Integer. If the web interface " +"displays alternating <quote>stripes</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1317 +msgid "" +"<envar role=\"rc-item-web\">style</envar>: Controls the template Mercurial " +"uses to display the web interface. Mercurial ships with two web templates, " +"named <literal>default</literal> and <literal>gitweb</literal> (the latter is " +"much more visually attractive). You can also specify a custom template of " +"your own; see <xref linkend=\"chap:template\"/> for details. Here, you can " +"see how to enable the <literal>gitweb</literal> style." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1330 +msgid "" +"<envar role=\"rc-item-web\">templates</envar>: Path. The directory in which " +"to search for template files. By default, Mercurial searches in the " +"directory in which it was installed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:1335 +msgid "" +"If you are using <filename role=\"special\">hgwebdir.cgi</filename>, you can " +"place a few configuration items in a <literal role=\"rc-web\">web</literal> " +"section of the <filename role=\"special\">hgweb.config</filename> file " +"instead of a <filename role=\"special\">~/.hgrc</filename> file, for " +"convenience. These items are <envar role=\"rc-item-web\">motd</envar> and " +"<envar role=\"rc-item-web\">style</envar>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:1346 +msgid "Options specific to an individual repository" +msgstr "针对单个版本库的选项" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1348 +msgid "" +"A few <literal role=\"rc-web\">web</literal> configuration items ought to be " +"placed in a repository's local <filename role=\"special\">.hg/hgrc</" +"filename>, rather than a user's or global <filename role=\"special\">~/.hgrc</" +"filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1353 +msgid "" +"<envar role=\"rc-item-web\">description</envar>: String. A free-form (but " +"preferably brief) string that describes the contents or purpose of the " +"repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1358 +msgid "" +"<envar role=\"rc-item-web\">name</envar>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:1366 +msgid "" +"Options specific to the <command role=\"hg-cmd\">hg serve</command> command" +msgstr "命令 <command role=\"hg-cmd\">hg serve</command> 的选项" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1369 +msgid "" +"Some of the items in the <literal role=\"rc-web\">web</literal> section of a " +"<filename role=\"special\">~/.hgrc</filename> file are only for use with the " +"<command role=\"hg-cmd\">hg serve</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1375 +msgid "" +"<envar role=\"rc-item-web\">accesslog</envar>: Path. The name of a file into " +"which to write an access log. By default, the <command role=\"hg-cmd\">hg " +"serve</command> command writes this information to standard output, not to a " +"file. Log entries are written in the standard <quote>combined</quote> file " +"format used by almost all web servers." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1383 +msgid "" +"<envar role=\"rc-item-web\">address</envar>: String. The local address on " +"which the server should listen for incoming connections. By default, the " +"server listens on all addresses." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1388 +msgid "" +"<envar role=\"rc-item-web\">errorlog</envar>: Path. The name of a file into " +"which to write an error log. By default, the <command role=\"hg-cmd\">hg " +"serve</command> command writes this information to standard error, not to a " +"file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1394 +msgid "" +"<envar role=\"rc-item-web\">ipv6</envar>: Boolean. Whether to use the IPv6 " +"protocol. By default, IPv6 is not used." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch05-collab.xml:1398 +msgid "" +"<envar role=\"rc-item-web\">port</envar>: Integer. The TCP port number on " +"which the server should listen. The default port number used is 8000." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch05-collab.xml:1405 +msgid "" +"Choosing the right <filename role=\"special\">~/.hgrc</filename> file to add " +"<literal role=\"rc-web\">web</literal> items to" +msgstr "" +"选择正确的 <filename role=\"special\"> ~/.hgrc</filename> 文件增加到 <literal " +"role=\"rc-web\">web</literal> 条目" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1409 +msgid "" +"It is important to remember that a web server like Apache or " +"<literal>lighttpd</literal> will run under a user ID that is different to " +"yours. CGI scripts run by your server, such as <filename role=\"special" +"\">hgweb.cgi</filename>, will usually also run under that user ID." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1416 +msgid "" +"If you add <literal role=\"rc-web\">web</literal> items to your own personal " +"<filename role=\"special\">~/.hgrc</filename> file, CGI scripts won't read " +"that <filename role=\"special\">~/.hgrc</filename> file. Those settings will " +"thus only affect the behaviour of the <command role=\"hg-cmd\">hg serve</" +"command> command when you run it. To cause CGI scripts to see your settings, " +"either create a <filename role=\"special\">~/.hgrc</filename> file in the " +"home directory of the user ID that runs your web server, or add those " +"settings to a system-wide <filename role=\"special\">~/.hgrc</filename> file." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch06-filenames.xml:5 +msgid "File names and pattern matching" +msgstr "文件名称与模式匹配" + +#. type: Content of: <book><chapter><para> +#: ../en/ch06-filenames.xml:7 +msgid "" +"Mercurial provides mechanisms that let you work with file names in a " +"consistent and expressive way." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:11 +msgid "Simple file naming" +msgstr "简单文件名称" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:13 +msgid "" +"Mercurial uses a unified piece of machinery <quote>under the hood</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:18 +msgid "" +"If you explicitly name real files on the command line, Mercurial works with " +"exactly those files, as you would expect. &interaction.filenames.files;" +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:22 +msgid "" +"When you provide a directory name, Mercurial will interpret this as " +"<quote>operate on every file in this directory and its subdirectories</" +"quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:33 +msgid "Running commands without any file names" +msgstr "不提供文件名称的执行命令" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:35 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:42 +msgid "" +"Most commands will operate on the entire working directory. This is what the " +"<command role=\"hg-cmd\">hg add</command> command does, for example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:46 +msgid "" +"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 " +"<command role=\"hg-cmd\">hg remove</command> with no arguments, for example." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:54 +msgid "" +"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 " +"<quote><filename class=\"directory\">.</filename></quote>." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:62 +msgid "" +"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 <command role=\"hg-cmd\">hg " +"status</command> 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 <command role=\"hg-cmd\">hg root</command> " +"command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:76 +msgid "Telling you what's going on" +msgstr "告诉你正在做什么" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:78 +msgid "" +"The <command role=\"hg-cmd\">hg add</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:85 +msgid "" +"The principle here is of <emphasis>least surprise</emphasis>. 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 <emphasis>implicitly</" +"emphasis>, because you provided no names, or a directory, or a pattern (see " +"below), it's safest to tell you what it's doing." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:92 +msgid "" +"For commands that behave this way, you can silence them using the <option " +"role=\"hg-opt-global\">-q</option> option. You can also get them to print " +"the name of every file, even those you've named explicitly, using the <option " +"role=\"hg-opt-global\">-v</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:100 +msgid "Using patterns to identify files" +msgstr "使用模式标识文件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:102 +msgid "" +"In addition to working with file and directory names, Mercurial lets you use " +"<emphasis>patterns</emphasis> to identify files. Mercurial's pattern " +"handling is expressive." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:106 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:113 +msgid "" +"To provide a pattern in place of a regular name on the command line, the " +"mechanism is simple:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:116 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:120 +msgid "" +"Mercurial supports two kinds of pattern syntax. The most frequently used is " +"called <literal>glob</literal>; this is the same kind of pattern matching " +"used by the Unix shell, and should be familiar to Windows command prompt " +"users, too." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:125 +msgid "" +"When Mercurial does automatic pattern matching on Windows, it uses " +"<literal>glob</literal> syntax. You can thus omit the <quote><literal>glob:</" +"literal></quote> prefix on Windows, but it's safe to use it, too." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:130 +msgid "" +"The <literal>re</literal> syntax is more powerful; it lets you specify " +"patterns using regular expressions, also known as regexps." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:134 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch06-filenames.xml:140 +msgid "Shell-style <literal>glob</literal> patterns" +msgstr "外壳风格的 <literal>glob</literal> 模式" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:142 +msgid "" +"This is an overview of the kinds of patterns you can use when you're matching " +"on glob patterns." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:145 +msgid "" +"The <quote><literal>*</literal></quote> character matches any string, within " +"a single directory." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:150 +msgid "" +"The <quote><literal>**</literal></quote> 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." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:157 +msgid "" +"The <quote><literal>?</literal></quote> pattern matches any single character." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:162 +msgid "" +"The <quote><literal>[</literal></quote> character begins a " +"<emphasis>character class</emphasis>. This matches any single character " +"within the class. The class ends with a <quote><literal>]</literal></quote> " +"character. A class may contain multiple <emphasis>range</emphasis>s of the " +"form <quote><literal>a-f</literal></quote>, which is shorthand for " +"<quote><literal>abcdef</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:172 +msgid "" +"If the first character after the <quote><literal>[</literal></quote> in a " +"character class is a <quote><literal>!</literal></quote>, it " +"<emphasis>negates</emphasis> the class, making it match any single character " +"not in the class." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:178 +msgid "" +"A <quote><literal>{</literal></quote> begins a group of subpatterns, where " +"the whole group matches if any subpattern in the group matches. The " +"<quote><literal>,</literal></quote> character separates subpatterns, and " +"<quote><literal>}</literal></quote> ends the group." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch06-filenames.xml:187 +msgid "Watch out!" +msgstr "千万小心!" + +# +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch06-filenames.xml:189 +msgid "" +"Don't forget that if you want to match a pattern in any directory, you should " +"not be using the <quote><literal>*</literal></quote> match-any token, as this " +"will only match within one directory. Instead, use the <quote><literal>**</" +"literal></quote> token. This small example illustrates the difference " +"between the two." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch06-filenames.xml:201 +msgid "Regular expression matching with <literal>re</literal> patterns" +msgstr "使用 <literal>re</literal> 模式的正则表达式匹配" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:204 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:210 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:217 +msgid "" +"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 " +"<filename class=\"directory\">foo</filename>, if you want to match files " +"under this directory, your pattern must start with <quote><literal>foo/</" +"literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:224 +msgid "" +"One thing to note, if you're familiar with Perl-style regexps, is that " +"Mercurial's are <emphasis>rooted</emphasis>. 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 <quote><literal>.*</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:234 +msgid "Filtering files" +msgstr "过滤文件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:236 +msgid "" +"Not only does Mercurial give you a variety of ways to specify files; it lets " +"you further winnow those files using <emphasis>filters</emphasis>. Commands " +"that work with file names accept two filtering options." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:241 +msgid "" +"<option role=\"hg-opt-global\">-I</option>, or <option role=\"hg-opt-global" +"\">--include</option>, lets you specify a pattern that file names must match " +"in order to be processed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:246 +msgid "" +"<option role=\"hg-opt-global\">-X</option>, or <option role=\"hg-opt-global" +"\">--exclude</option>, gives you a way to <emphasis>avoid</emphasis> " +"processing files, if they match this pattern." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:251 +msgid "" +"You can provide multiple <option role=\"hg-opt-global\">-I</option> and " +"<option role=\"hg-opt-global\">-X</option> 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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:258 +msgid "" +"You can read a <option role=\"hg-opt-global\">-I</option> filter as " +"<quote>process only the files that match this filter</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:264 +msgid "" +"The <option role=\"hg-opt-global\">-X</option> filter is best read as " +"<quote>process only the files that don't match this pattern</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:272 +msgid "Ignoring unwanted files and directories" +msgstr "忽略不需要的文件和目录" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:274 +msgid "XXX." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch06-filenames.xml:278 +msgid "Case sensitivity" +msgstr "大小写敏感性" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:280 +msgid "" +"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 (<quote>N</quote> " +"versus <quote>n</quote>) 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:289 +msgid "" +"Operating systems and filesystems differ in the way they handle the " +"<emphasis>case</emphasis> of characters in file and directory names. There " +"are three common ways to handle case in names." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:294 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:299 +msgid "" +"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</filename> and <filename>FoO</filename> identify the same " +"file. This treatment of uppercase and lowercase letters as interchangeable " +"is also referred to as <emphasis>case folding</emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch06-filenames.xml:310 +msgid "" +"Case sensitive. The case of a name is significant at all times. The names " +"<filename>foo</filename> and {FoO} identify different files. This is the way " +"Linux and Unix systems normally work." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch06-filenames.xml:316 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch06-filenames.xml:323 +msgid "Safe, portable repository storage" +msgstr "安全,可移植的版本库存储" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:325 +msgid "" +"Mercurial's repository storage mechanism is <emphasis>case safe</emphasis>. " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch06-filenames.xml:336 +msgid "Detecting case conflicts" +msgstr "检测大小写冲突" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:338 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:344 +msgid "" +"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</filename> and the other named <filename>MyFile.C</" +"filename>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:355 +msgid "" +"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 <command role=\"hg-cmd\">hg update</command> the " +"working directory to that changeset, or <command role=\"hg-cmd\">hg merge</" +"command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch06-filenames.xml:367 +msgid "Fixing a case conflict" +msgstr "修正大小写冲突" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:369 +msgid "" +"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 <command role=\"hg-cmd\">hg update</command> or " +"<command role=\"hg-cmd\">hg merge</command>, the procedure to fix the problem " +"is simple." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:376 +msgid "" +"Just find a nearby Linux or Unix box, clone the problem repository onto it, " +"and use Mercurial's <command role=\"hg-cmd\">hg rename</command> command to " +"change the names of any offending files or directories so that they will no " +"longer cause case folding conflicts. Commit this change, <command role=\"hg-" +"cmd\">hg pull</command> or <command role=\"hg-cmd\">hg push</command> it " +"across to your Windows or MacOS system, and <command role=\"hg-cmd\">hg " +"update</command> to the revision with the non-conflicting names." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch06-filenames.xml:386 +msgid "" +"The changeset with case-conflicting names will remain in your project's " +"history, and you still won't be able to <command role=\"hg-cmd\">hg update</" +"command> your working directory to that changeset on a Windows or MacOS " +"system, but you can continue development unimpeded." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch06-filenames.xml:393 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch07-branch.xml:5 +msgid "Managing releases and branchy development" +msgstr "发布管理与分支开发" + +#. type: Content of: <book><chapter><para> +#: ../en/ch07-branch.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch07-branch.xml:12 +msgid "" +"Many software projects issue periodic <quote>major</quote> releases that " +"contain substantial new features. In parallel, they may issue <quote>minor</" +"quote> releases. These are usually identical to the major releases off which " +"they're based, but with a few bugs fixed." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch07-branch.xml:18 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:25 +msgid "Giving a persistent name to a revision" +msgstr "给版本指定一个永久的名称" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:27 +msgid "" +"Once you decide that you'd like to call a particular revision a " +"<quote>release</quote>, 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;" +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:34 +msgid "" +"Mercurial lets you give a permanent name to any revision using the <command " +"role=\"hg-cmd\">hg tag</command> command. Not surprisingly, these names are " +"called <quote>tags</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:40 +msgid "" +"A tag is nothing more than a <quote>symbolic name</quote> 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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch07-branch.xml:49 +msgid "Colon (ASCII 58, <quote><literal>:</literal></quote>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch07-branch.xml:52 +msgid "Carriage return (ASCII 13, <quote><literal>\\r</literal></quote>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch07-branch.xml:55 +msgid "Newline (ASCII 10, <quote><literal>\\n</literal></quote>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:59 +msgid "" +"You can use the <command role=\"hg-cmd\">hg tags</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:67 +msgid "" +"Notice that <literal>tip</literal> is listed in the output of <command role=" +"\"hg-cmd\">hg tags</command>. The <literal>tip</literal> tag is a special " +"<quote>floating</quote> tag, which always identifies the newest revision in " +"the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:73 +msgid "" +"In the output of the <command role=\"hg-cmd\">hg tags</command> 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 <literal>tip</" +"literal> is always going to be the first tag listed in the output of <command " +"role=\"hg-cmd\">hg tags</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:80 +msgid "" +"When you run <command role=\"hg-cmd\">hg log</command>, if it displays a " +"revision that has tags associated with it, it will print those tags." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:86 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:93 +msgid "" +"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 <quote>too many</quote> (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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:101 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:109 +msgid "" +"If you want to remove a tag that you no longer want, use <command role=\"hg-" +"cmd\">hg tag --remove</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:114 +msgid "" +"You can also modify a tag at any time, so that it identifies a different " +"revision, by simply issuing a new <command role=\"hg-cmd\">hg tag</command> " +"command. You'll have to use the <option role=\"hg-opt-tag\">-f</option> " +"option to tell Mercurial that you <emphasis>really</emphasis> want to update " +"the tag." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:123 +msgid "" +"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." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:129 +msgid "" +"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 " +"<filename role=\"special\">.hgtags</filename>. When you run the <command " +"role=\"hg-cmd\">hg tag</command> command, Mercurial modifies this file, then " +"automatically commits the change to it. This means that every time you run " +"<command role=\"hg-cmd\">hg tag</command>, you'll see a corresponding " +"changeset in the output of <command role=\"hg-cmd\">hg log</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch07-branch.xml:142 +msgid "Handling tag conflicts during a merge" +msgstr "在合并期间处理标签冲突" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:144 +msgid "" +"You won't often need to care about the <filename role=\"special\">.hgtags</" +"filename> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:151 +msgid "" +"If you're resolving a conflict in the <filename role=\"special\">.hgtags</" +"filename> file during a merge, there's one twist to modifying the <filename " +"role=\"special\">.hgtags</filename> file: when Mercurial is parsing the tags " +"in a repository, it <emphasis>never</emphasis> reads the working copy of the " +"<filename role=\"special\">.hgtags</filename> file. Instead, it reads the " +"<emphasis>most recently committed</emphasis> revision of the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:161 +msgid "" +"An unfortunate consequence of this design is that you can't actually verify " +"that your merged <filename role=\"special\">.hgtags</filename> file is " +"correct until <emphasis>after</emphasis> you've committed a change. So if " +"you find yourself resolving a conflict on <filename role=\"special\">.hgtags</" +"filename> during a merge, be sure to run <command role=\"hg-cmd\">hg tags</" +"command> after you commit. If it finds an error in the <filename role=" +"\"special\">.hgtags</filename> file, it will report the location of the " +"error, which you can then fix and commit. You should then run <command role=" +"\"hg-cmd\">hg tags</command> again, just to be sure that your fix is correct." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch07-branch.xml:176 +msgid "Tags and cloning" +msgstr "标签与克隆" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:178 +msgid "" +"You may have noticed that the <command role=\"hg-cmd\">hg clone</command> " +"command has a <option role=\"hg-opt-clone\">-r</option> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:186 +msgid "" +"Recall that a tag is stored as a revision to the <filename role=\"special\">." +"hgtags</filename> file, so that when you create a tag, the changeset in which " +"it's recorded necessarily refers to an older changeset. When you run " +"<command role=\"hg-cmd\">hg clone -r foo</command> to clone a repository as " +"of tag <literal>foo</literal>, the new clone <emphasis>will not contain the " +"history that created the tag</emphasis> 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 <emphasis>not</emphasis> the tag " +"you might have expected." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch07-branch.xml:201 +msgid "When permanent tags are too much" +msgstr "当永久标签太多的时候" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:203 +msgid "" +"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 " +"<literal>4237e45506ee</literal> is really <literal>v2.0.2</literal>. If " +"you're trying to track down a subtle bug, you might want a tag to remind you " +"of something like <quote>Anne saw the symptoms with this revision</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch07-branch.xml:213 +msgid "" +"For cases like this, what you might want to use are <emphasis>local</" +"emphasis> tags. You can create a local tag with the <option role=\"hg-opt-tag" +"\">-l</option> option to the <command role=\"hg-cmd\">hg tag</command> " +"command. This will store the tag in a file called <filename role=\"special" +"\">.hg/localtags</filename>. Unlike <filename role=\"special\">.hgtags</" +"filename>, <filename role=\"special\">.hg/localtags</filename> is not " +"revision controlled. Any tags you create using <option role=\"hg-opt-tag\">-" +"l</option> remain strictly local to the repository you're currently working " +"in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:228 +msgid "The flow of changes&emdash;big picture vs. little" +msgstr "修改流程—宏观与微观" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:230 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:234 +msgid "" +"There might be a push for a new <quote>main</quote> release; a new minor " +"bugfix release to the last main release; and an unexpected <quote>hot fix</" +"quote> to an old release that is now in maintenance mode." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:239 +msgid "" +"The usual way people refer to these different concurrent directions of " +"development is as <quote>branches</quote>. However, we've already seen " +"numerous times that Mercurial treats <emphasis>all of history</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch07-branch.xml:246 +msgid "" +"<quote>Big picture</quote> branches represent the sweep of a project's " +"evolution; people give them names, and talk about them in conversation." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch07-branch.xml:250 +msgid "" +"<quote>Little picture</quote> branches are artefacts of the day-to-day " +"activity of developing and merging changes. They expose the narrative of how " +"the code was developed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:258 +msgid "Managing big-picture branches in repositories" +msgstr "在版本库中管理分支" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:260 +msgid "" +"The easiest way to isolate a <quote>big picture</quote> branch in Mercurial " +"is in a dedicated repository. If you have an existing shared " +"repository&emdash;let's call it <literal>myproject</literal>&emdash;that " +"reaches a <quote>1.0</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:270 +msgid "" +"You can then clone a new shared <literal>myproject-1.0.1</literal> repository " +"as of that tag." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:276 +msgid "" +"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 <literal>myproject-1.0.1</" +"literal> repository, make their changes, and push them back." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:283 +msgid "" +"Meanwhile, development for the next major release can continue, isolated and " +"unabated, in the <literal>myproject</literal> repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:291 +msgid "Don't repeat yourself: merging across branches" +msgstr "不要重复劳动:在分支间合并" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:293 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:301 +msgid "" +"In the simplest instance, all you need to do is pull changes from your " +"maintenance branch into your local clone of the target branch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:307 +msgid "" +"You'll then need to merge the heads of the two branches, and push back to the " +"main branch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:314 +msgid "Naming branches within one repository" +msgstr "版本库中的命名分支" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:316 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:323 +msgid "" +"If you're more in the <quote>power user</quote> category (<emphasis>and</" +"emphasis> 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 <quote>small picture</quote> and <quote>big " +"picture</quote> branches. While Mercurial works with multiple <quote>small " +"picture</quote> branches in a repository all the time (for example after you " +"pull changes in, but before you merge them), it can <emphasis>also</emphasis> " +"work with multiple <quote>big picture</quote> branches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:334 +msgid "" +"The key to working this way is that Mercurial lets you assign a persistent " +"<emphasis>name</emphasis> to a branch. There always exists a branch named " +"<literal>default</literal>. Even before you start naming branches yourself, " +"you can find traces of the <literal>default</literal> branch if you look for " +"them." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:341 +msgid "" +"As an example, when you run the <command role=\"hg-cmd\">hg commit</command> " +"command, and it pops up your editor so that you can enter a commit message, " +"look for a line that contains the text <quote><literal>HG: branch default</" +"literal></quote> at the bottom. This is telling you that your commit will " +"occur on the branch named <literal>default</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:348 +msgid "" +"To start working with named branches, use the <command role=\"hg-cmd\">hg " +"branches</command> command. This command lists the named branches already " +"present in your repository, telling you which changeset is the tip of each." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:355 +msgid "" +"Since you haven't created any named branches yet, the only one that exists is " +"<literal>default</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:358 +msgid "" +"To find out what the <quote>current</quote> branch is, run the <command role=" +"\"hg-cmd\">hg branch</command> command, giving it no arguments. This tells " +"you what branch the parent of the current changeset is on." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:365 +msgid "" +"To create a new branch, run the <command role=\"hg-cmd\">hg branch</command> " +"command again. This time, give it one argument: the name of the branch you " +"want to create." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:371 +msgid "" +"After you've created a branch, you might wonder what effect the <command role=" +"\"hg-cmd\">hg branch</command> command has had. What do the <command role=" +"\"hg-cmd\">hg status</command> and <command role=\"hg-cmd\">hg tip</command> " +"commands report?" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:378 +msgid "" +"Nothing has changed in the working directory, and there's been no new history " +"created. As this suggests, running the <command role=\"hg-cmd\">hg branch</" +"command> command has no permanent effect; it only tells Mercurial what branch " +"name to use the <emphasis>next</emphasis> time you commit a changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:385 +msgid "" +"When you commit a change, Mercurial records the name of the branch on which " +"you committed. Once you've switched from the <literal>default</literal> " +"branch to another and committed, you'll see the name of the new branch show " +"up in the output of <command role=\"hg-cmd\">hg log</command>, <command role=" +"\"hg-cmd\">hg tip</command>, and other commands that display the same kind of " +"output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:395 +msgid "" +"The <command role=\"hg-cmd\">hg log</command>-like commands will print the " +"branch name of every changeset that's not on the <literal>default</literal> " +"branch. As a result, if you never use named branches, you'll never see this " +"information." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:400 +msgid "" +"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 <command " +"role=\"hg-cmd\">hg branch</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:408 +msgid "" +"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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:414 +msgid "Dealing with multiple named branches in a repository" +msgstr "在版本库中处理多个命名分支" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:417 +msgid "" +"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 <command role=\"hg-cmd\">hg update</command> or <command role=\"hg-cmd" +"\">hg pull -u</command>. It will update the working directory to the tip of " +"this branch, no matter what the <quote>repo-wide</quote> tip is. To update " +"to a revision that's on a different named branch, you may need to use the " +"<option role=\"hg-opt-update\">-C</option> option to <command role=\"hg-cmd" +"\">hg update</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:427 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:433 +msgid "" +"We're on the <literal>bar</literal> branch, but there also exists an older " +"<command role=\"hg-cmd\">hg foo</command> branch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:437 +msgid "" +"We can <command role=\"hg-cmd\">hg update</command> back and forth between " +"the tips of the <literal>foo</literal> and <literal>bar</literal> branches " +"without needing to use the <option role=\"hg-opt-update\">-C</option> option, " +"because this only involves going backwards and forwards linearly through our " +"change history." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:446 +msgid "" +"If we go back to the <literal>foo</literal> branch and then run <command role=" +"\"hg-cmd\">hg update</command>, it will keep us on <literal>foo</literal>, " +"not move us to the tip of <literal>bar</literal>." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:453 +msgid "" +"Committing a new change on the <literal>foo</literal> branch introduces a new " +"head." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:460 +msgid "Branch names and merging" +msgstr "分支名称与合并" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:462 +msgid "" +"As you've probably noticed, merges in Mercurial are not symmetrical. Let's " +"say our repository has two heads, 17 and 23. If I <command role=\"hg-cmd" +"\">hg update</command> to 17 and then <command role=\"hg-cmd\">hg merge</" +"command> with 23, Mercurial records 17 as the first parent of the merge, and " +"23 as the second. Whereas if I <command role=\"hg-cmd\">hg update</command> " +"to 23 and then <command role=\"hg-cmd\">hg merge</command> with 17, it " +"records 23 as the first parent, and 17 as the second." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:472 +msgid "" +"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 " +"<literal>foo</literal>, and you merge with <literal>bar</literal>, the branch " +"name will still be <literal>foo</literal> after you merge." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:479 +msgid "" +"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 <literal>foo</literal> " +"branch, and so are you. We commit different changes; I pull your changes; I " +"now have two heads, each claiming to be on the <literal>foo</literal> " +"branch. The result of a merge will be a single head on the <literal>foo</" +"literal> branch, as you might hope." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:487 +msgid "" +"But if I'm working on the <literal>bar</literal> branch, and I merge work " +"from the <literal>foo</literal> branch, the result will remain on the " +"<literal>bar</literal> branch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:493 +msgid "" +"To give a more concrete example, if I'm working on the <literal>bleeding-" +"edge</literal> branch, and I want to bring in the latest fixes from the " +"<literal>stable</literal> branch, Mercurial will choose the <quote>right</" +"quote> (<literal>bleeding-edge</literal>) branch name when I pull and merge " +"from <literal>stable</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch07-branch.xml:502 +msgid "Branch naming is generally useful" +msgstr "分支名称通常都很有用" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:504 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:509 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch07-branch.xml:514 +msgid "" +"If you're working with shared repositories, you can set up a <literal role=" +"\"hook\">pretxnchangegroup</literal> hook on each that will block incoming " +"changes that have the <quote>wrong</quote> branch name. This provides a " +"simple, but effective, defence against people accidentally pushing changes " +"from a <quote>bleeding edge</quote> branch to a <quote>stable</quote> " +"branch. Such a hook might look like this inside the shared repo's <filename " +"role=\"special\"> /.hgrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch08-undo.xml:5 +msgid "Finding and fixing mistakes" +msgstr "查找和修改错误" + +#. type: Content of: <book><chapter><para> +#: ../en/ch08-undo.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:15 +msgid "Erasing local history" +msgstr "销毁本地历史" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:18 +msgid "The accidental commit" +msgstr "意外的提交" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:20 +msgid "" +"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 <command role=\"hg-cmd\">hg add</command> it. A <quote>plain " +"wrong</quote> changeset is not as common, but no less annoying." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:31 +msgid "Rolling back a transaction" +msgstr "回滚一个事务" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:33 +msgid "" +"In <xref linkend=\"sec:concepts:txn\"/>, I mentioned that Mercurial treats " +"each modification of a repository as a <emphasis>transaction</emphasis>. " +"Every time you commit a changeset or pull changes from another repository, " +"Mercurial remembers what you did. You can undo, or <emphasis>roll back</" +"emphasis>, exactly one of these actions using the <command role=\"hg-cmd\">hg " +"rollback</command> command. (See <xref linkend=\"sec:undo:rollback-after-push" +"\"/> for an important caveat about the use of this command.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:43 +msgid "" +"Here's a mistake that I often find myself making: committing a change in " +"which I've created a new file, but forgotten to <command role=\"hg-cmd\">hg " +"add</command> it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:50 +msgid "" +"Looking at the output of <command role=\"hg-cmd\">hg status</command> after " +"the commit immediately confirms the error." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:56 +msgid "" +"The commit captured the changes to the file <filename>a</filename>, but not " +"the new file <filename>b</filename>. 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</filename> would refer to <filename>b</filename>, " +"which would not be present in their repository when they pulled my changes. " +"I would thus become the object of some indignation." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:65 +msgid "" +"However, luck is with me&emdash;I've caught my error before I pushed the " +"changeset. I use the <command role=\"hg-cmd\">hg rollback</command> command, " +"and Mercurial makes that last changeset vanish." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:72 +msgid "" +"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</" +"filename> 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 <command role=\"hg-cmd\">hg add</" +"command> the file <filename>b</filename>, and rerun my commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:85 +msgid "The erroneous pull" +msgstr "错误的抓取" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:87 +msgid "" +"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 <quote>0.9</quote> release, and another, " +"containing different changes, for the <quote>1.0</quote> release." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:94 +msgid "" +"Given this, you can imagine that the consequences could be messy if you had a " +"local <quote>0.9</quote> repository, and accidentally pulled changes from the " +"shared <quote>1.0</quote> repository into it. At worst, you could be paying " +"insufficient attention, and push those changes into the shared <quote>0.9</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:106 +msgid "" +"The <command role=\"hg-cmd\">hg rollback</command> command will work nicely " +"to expunge all of the changesets that you just pulled. Mercurial groups all " +"changes from one <command role=\"hg-cmd\">hg pull</command> into a single " +"transaction, so one <command role=\"hg-cmd\">hg rollback</command> is all you " +"need to undo this mistake." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:115 +msgid "Rolling back is useless once you've pushed" +msgstr "当完成推送后,回滚是无效的" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:117 +msgid "" +"The value of the <command role=\"hg-cmd\">hg rollback</command> command drops " +"to zero once you've pushed your changes to another repository. Rolling back " +"a change makes it disappear entirely, but <emphasis>only</emphasis> in the " +"repository in which you perform the <command role=\"hg-cmd\">hg rollback</" +"command>. Because a rollback eliminates history, there's no way for the " +"disappearance of a change to propagate between repositories." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:126 +msgid "" +"If you've pushed a change to another repository&emdash;particularly if it's a " +"shared repository&emdash;it has essentially <quote>escaped into the wild,</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:135 +msgid "" +"(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, <emphasis>and</" +"emphasis> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:147 +msgid "You can only roll back once" +msgstr "你只能回滚一次" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:149 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:158 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:165 +msgid "Reverting the mistaken change" +msgstr "撤销错误的修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:167 +msgid "" +"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 " +"<command role=\"hg-cmd\">hg revert</command> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:176 +msgid "" +"Let's illustrate how the <command role=\"hg-cmd\">hg revert</command> command " +"works with yet another small example. We'll begin by modifying a file that " +"Mercurial is already tracking." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:183 +msgid "" +"If we don't want that change, we can simply <command role=\"hg-cmd\">hg " +"revert</command> the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:189 +msgid "" +"The <command role=\"hg-cmd\">hg revert</command> command provides us with an " +"extra degree of safety by saving our modified file with a <filename>.orig</" +"filename> extension." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:196 +msgid "" +"Here is a summary of the cases that the <command role=\"hg-cmd\">hg revert</" +"command> command can deal with. We will describe each of these in more " +"detail in the section that follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:201 +msgid "If you modify a file, it will restore the file to its unmodified state." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:204 +msgid "" +"If you <command role=\"hg-cmd\">hg add</command> a file, it will undo the " +"<quote>added</quote> state of the file, but leave the file itself untouched." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:208 +msgid "" +"If you delete a file without telling Mercurial, it will restore the file to " +"its unmodified contents." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:211 +msgid "" +"If you use the <command role=\"hg-cmd\">hg remove</command> command to remove " +"a file, it will undo the <quote>removed</quote> state of the file, and " +"restore the file to its unmodified contents." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:218 +msgid "File management errors" +msgstr "文件管理错误" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:220 +msgid "" +"The <command role=\"hg-cmd\">hg revert</command> command is useful for more " +"than just modified files. It lets you reverse the results of all of " +"Mercurial's file management commands&emdash;<command role=\"hg-cmd\">hg add</" +"command>, <command role=\"hg-cmd\">hg remove</command>, and so on." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:226 +msgid "" +"If you <command role=\"hg-cmd\">hg add</command> a file, then decide that in " +"fact you don't want Mercurial to track it, use <command role=\"hg-cmd\">hg " +"revert</command> to undo the add. Don't worry; Mercurial will not modify the " +"file in any way. It will just <quote>unmark</quote> the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:234 +msgid "" +"Similarly, if you ask Mercurial to <command role=\"hg-cmd\">hg remove</" +"command> a file, you can use <command role=\"hg-cmd\">hg revert</command> 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 <quote>missing</quote>)." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:245 +msgid "" +"If you revert a <command role=\"hg-cmd\">hg copy</command>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch08-undo.xml:254 +msgid "A slightly special case: reverting a rename" +msgstr "一个稍微特别的案例:撤销改名" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:256 +msgid "" +"If you <command role=\"hg-cmd\">hg rename</command> a file, there is one " +"small detail that you should remember. When you <command role=\"hg-cmd\">hg " +"revert</command> a rename, it's not enough to provide the name of the renamed-" +"to file, as you can see here." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:264 +msgid "" +"As you can see from the output of <command role=\"hg-cmd\">hg status</" +"command>, the renamed-to file is no longer identified as added, but the " +"renamed-<emphasis>from</emphasis> file is still removed! This is counter-" +"intuitive (at least to me), but at least it's easy to deal with." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:273 +msgid "" +"So remember, to revert a <command role=\"hg-cmd\">hg rename</command>, you " +"must provide <emphasis>both</emphasis> the source and destination names." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:278 +msgid "% TODO: the output doesn't look like it will be removed!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:281 +msgid "" +"(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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:288 +msgid "" +"These fiddly aspects of reverting a rename arguably constitute a small bug in " +"Mercurial." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:295 +msgid "Dealing with committed changes" +msgstr "处理已经提交的修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:297 +msgid "" +"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 <quote>back out</quote> an entire changeset automatically, and building " +"blocks that let you reverse part of a changeset by hand." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:303 +msgid "" +"Before you read this section, here's something to keep in mind: the <command " +"role=\"hg-cmd\">hg backout</command> command undoes changes by " +"<emphasis>adding</emphasis> 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 " +"<xref linkend=\"sec:undo:aaaiiieee\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:312 +msgid "Backing out a changeset" +msgstr "恢复一个修改集" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:314 +msgid "" +"The <command role=\"hg-cmd\">hg backout</command> command lets you " +"<quote>undo</quote> the effects of an entire changeset in an automated " +"fashion. Because Mercurial's history is immutable, this command " +"<emphasis>does not</emphasis> get rid of the changeset you want to undo. " +"Instead, it creates a new changeset that <emphasis>reverses</emphasis> the " +"effect of the to-be-undone changeset." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:323 +msgid "" +"The operation of the <command role=\"hg-cmd\">hg backout</command> command is " +"a little intricate, so let's illustrate it with some examples. First, we'll " +"create a repository with some simple changes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:330 +msgid "" +"The <command role=\"hg-cmd\">hg backout</command> command takes a single " +"changeset ID as its argument; this is the changeset to back out. Normally, " +"<command role=\"hg-cmd\">hg backout</command> will drop you into a text " +"editor to write a commit message, so you can record why you're backing the " +"change out. In this example, we provide a commit message on the command line " +"using the <option role=\"hg-opt-backout\">-m</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:341 +msgid "Backing out the tip changeset" +msgstr "恢复顶点修改集" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:343 +msgid "We're going to start by backing out the last changeset we committed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:348 +msgid "" +"You can see that the second line from <filename>myfile</filename> is no " +"longer present. Taking a look at the output of <command role=\"hg-cmd\">hg " +"log</command> gives us an idea of what the <command role=\"hg-cmd\">hg " +"backout</command> command has done. &interaction.backout.simple.log; Notice " +"that the new changeset that <command role=\"hg-cmd\">hg backout</command> has " +"created is a child of the changeset we backed out. It's easier to see this " +"in <xref linkend=\"fig:undo:backout\"/>, which presents a graphical view of " +"the change history. As you can see, the history is nice and linear." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch08-undo.xml:361 ../en/ch08-undo.xml:473 +msgid "" +"Backing out a change using the <command role=\"hg-cmd\">hg backout</command> " +"command" +msgstr "使用 <command role=\"hg-cmd\">hg backout</command> 恢复一个修改" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch08-undo.xml:364 +msgid "<imageobject><imagedata fileref=\"figs/undo-simple.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:371 +msgid "Backing out a non-tip change" +msgstr "恢复非顶点的修改" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:373 +msgid "" +"If you want to back out a change other than the last one you committed, pass " +"the <option role=\"hg-opt-backout\">--merge</option> option to the <command " +"role=\"hg-cmd\">hg backout</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:380 +msgid "" +"This makes backing out any changeset a <quote>one-shot</quote> operation " +"that's usually simple and fast." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:386 +msgid "" +"If you take a look at the contents of <filename>myfile</filename> after the " +"backout finishes, you'll see that the first and third changes are present, " +"but not the second." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:393 +msgid "" +"As the graphical history in <xref linkend=\"fig:undo:backout-non-tip\"/> " +"illustrates, Mercurial actually commits <emphasis>two</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:404 +msgid "" +"% TODO: to me it looks like mercurial doesn't commit the second merge " +"automatically!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch08-undo.xml:408 +msgid "" +"Automated backout of a non-tip change using the <command role=\"hg-cmd\">hg " +"backout</command> command" +msgstr "使用 <command role=\"hg-cmd\">hg backout</command> 自动恢复非顶点的修改" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch08-undo.xml:411 +msgid "" +"<imageobject><imagedata fileref=\"figs/undo-non-tip.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:416 +msgid "" +"The result is that you end up <quote>back where you were</quote>, only with " +"some extra history that undoes the effect of the changeset you wanted to back " +"out." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch08-undo.xml:421 +msgid "Always use the <option role=\"hg-opt-backout\">--merge</option> option" +msgstr "始终使用选项 <option role=\"hg-opt-backout\">--merge</option>" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch08-undo.xml:424 +msgid "" +"In fact, since the <option role=\"hg-opt-backout\">--merge</option> option " +"will do the <quote>right thing</quote> 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 <emphasis>always</emphasis> use this " +"option when you run the <command role=\"hg-cmd\">hg backout</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:435 +msgid "Gaining more control of the backout process" +msgstr "在恢复处理中获得更多控制" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:437 +msgid "" +"While I've recommended that you always use the <option role=\"hg-opt-backout" +"\">--merge</option> option when backing out a change, the <command role=\"hg-" +"cmd\">hg backout</command> 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 <command " +"role=\"hg-cmd\">hg backout</command> command is doing for you automatically. " +"To illustrate this, let's clone our first repository, but omit the backout " +"change that it contains." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:450 +msgid "" +"As with our earlier example, We'll commit a third changeset, then back out " +"its parent, and see what happens." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:456 +msgid "" +"Our new changeset is again a descendant of the changeset we backout out; it's " +"thus a new head, <emphasis>not</emphasis> a descendant of the changeset that " +"was the tip. The <command role=\"hg-cmd\">hg backout</command> command was " +"quite explicit in telling us this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:464 +msgid "" +"Again, it's easier to see what has happened by looking at a graph of the " +"revision history, in <xref linkend=\"fig:undo:backout-manual\"/>. This makes " +"it clear that when we use <command role=\"hg-cmd\">hg backout</command> to " +"back out a change other than the tip, Mercurial adds a new head to the " +"repository (the change it committed is box-shaped)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch08-undo.xml:476 +msgid "<imageobject><imagedata fileref=\"figs/undo-manual.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:481 +msgid "" +"After the <command role=\"hg-cmd\">hg backout</command> command has " +"completed, it leaves the new <quote>backout</quote> changeset as the parent " +"of the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:488 +msgid "Now we have two isolated sets of changes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:492 +msgid "" +"Let's think about what we expect to see as the contents of <filename>myfile</" +"filename> 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 <emphasis>don't</emphasis> expect to see the third change present in " +"<filename>myfile</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:502 +msgid "" +"To get the third change back into the file, we just do a normal merge of our " +"two heads." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:507 +msgid "" +"Afterwards, the graphical history of our repository looks like <xref linkend=" +"\"fig:undo:backout-manual-merge\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch08-undo.xml:512 +msgid "Manually merging a backout change" +msgstr "手工合并恢复修改" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch08-undo.xml:514 +msgid "" +"<imageobject><imagedata fileref=\"figs/undo-manual-merge.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:521 +msgid "Why <command role=\"hg-cmd\">hg backout</command> works as it does" +msgstr "<command role=\"hg-cmd\">hg backout</command> 的内幕" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:524 +msgid "" +"Here's a brief description of how the <command role=\"hg-cmd\">hg backout</" +"command> command works." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:527 +msgid "" +"It ensures that the working directory is <quote>clean</quote>, i.e. that the " +"output of <command role=\"hg-cmd\">hg status</command> would be empty." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:531 +msgid "" +"It remembers the current parent of the working directory. Let's call this " +"changeset <literal>orig</literal>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:535 +msgid "" +"It does the equivalent of a <command role=\"hg-cmd\">hg update</command> to " +"sync the working directory to the changeset you want to back out. Let's call " +"this changeset <literal>backout</literal>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:540 +msgid "" +"It finds the parent of that changeset. Let's call that changeset " +"<literal>parent</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:543 +msgid "" +"For each file that the <literal>backout</literal> changeset affected, it does " +"the equivalent of a <command role=\"hg-cmd\">hg revert -r parent</command> on " +"that file, to restore it to the contents it had before that changeset was " +"committed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:550 +msgid "" +"It commits the result as a new changeset. This changeset has " +"<literal>backout</literal> as its parent." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:554 +msgid "" +"If you specify <option role=\"hg-opt-backout\">--merge</option> on the " +"command line, it merges with <literal>orig</literal>, and commits the result " +"of the merge." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:560 +msgid "" +"An alternative way to implement the <command role=\"hg-cmd\">hg backout</" +"command> command would be to <command role=\"hg-cmd\">hg export</command> the " +"to-be-backed-out changeset as a diff, then use the <option role=\"cmd-opt-" +"patch\">--reverse</option> option to the <command>patch</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:570 +msgid "" +"The reason that <command role=\"hg-cmd\">hg backout</command> 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 <emphasis>between</" +"emphasis> the change you're backing out and the current tip." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:577 +msgid "" +"If you're backing out a changeset that's 100 revisions back in your project's " +"history, the chances that the <command>patch</command> command will be able " +"to apply a reverse diff cleanly are not good, because intervening changes are " +"likely to have <quote>broken the context</quote> that <command>patch</" +"command> uses to determine whether it can apply a patch (if this sounds like " +"gibberish, see <xref linkend=\"sec:mq:patch\"/> for a discussion of the " +"<command>patch</command> 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</command> can " +"deal with." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:594 +msgid "Changes that should never have been" +msgstr "不该发生的修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:596 +msgid "" +"Most of the time, the <command role=\"hg-cmd\">hg backout</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:602 +msgid "" +"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 <emphasis>big</emphasis>, so they increase " +"the size of the repository and the amount of time it takes to clone or pull " +"changes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:611 +msgid "" +"Before I discuss the options that you have if you commit a <quote>brown paper " +"bag</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:616 +msgid "" +"Since Mercurial treats history as accumulative&emdash;every change builds on " +"top of all changes that preceded it&emdash;you generally can't just make " +"disastrous changes disappear. The one exception is when you've just " +"committed a change, and it hasn't been pushed or pulled into another " +"repository. That's when you can safely use the <command role=\"hg-cmd\">hg " +"rollback</command> command, as I detailed in <xref linkend=\"sec:undo:rollback" +"\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:625 +msgid "" +"After you've pushed a bad change to another repository, you <emphasis>could</" +"emphasis> still use <command role=\"hg-cmd\">hg rollback</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:633 +msgid "" +"If a situation like this arises, and you know which repositories your bad " +"change has propagated into, you can <emphasis>try</emphasis> to get rid of " +"the changeefrom <emphasis>every</emphasis> 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 <quote>in the wild</" +"quote>, and could propagate further." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:641 +msgid "" +"If you've committed one or more changes <emphasis>after</emphasis> the change " +"that you'd like to see disappear, your options are further reduced. Mercurial " +"doesn't provide a way to <quote>punch a hole</quote> in history, leaving " +"changesets intact." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:647 +msgid "" +"XXX This needs filling out. The <literal>hg-replay</literal> script in the " +"<literal>examples</literal> directory works, but doesn't handle merge " +"changesets. Kind of an important omission." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:653 +msgid "Protect yourself from <quote>escaped</quote> changes" +msgstr "使用<quote>校验</quote>修改来保护你自己" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:656 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:663 +msgid "" +"By configuring some hooks on that repository to validate incoming changesets " +"(see chapter <xref linkend=\"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 <quote>die out</quote> because they can't propagate into " +"the central repository. Better yet, this happens without any need for " +"explicit intervention." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:673 +msgid "" +"For instance, an incoming change hook that verifies that a changeset will " +"actually compile can prevent people from inadvertantly <quote>breaking the " +"build</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:680 +msgid "Finding the source of a bug" +msgstr "查找问题的根源" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:682 +msgid "" +"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 <command role=\"hg-cmd\">hg bisect</" +"command>, that helps you to automate this process and accomplish it very " +"efficiently." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:689 +msgid "" +"The idea behind the <command role=\"hg-cmd\">hg bisect</command> 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 <command role=\"hg-cmd\">hg bisect</command> command uses your test to " +"direct its search for the changeset that introduced the code that caused the " +"bug." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:698 +msgid "" +"Here are a few scenarios to help you understand how you might apply this " +"command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:701 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:706 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:713 +msgid "" +"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 <quote>fast</quote> or <quote>slow</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:720 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:725 +msgid "" +"From these examples, it should be clear that the <command role=\"hg-cmd\">hg " +"bisect</command> command is not useful only for finding the sources of bugs. " +"You can use it to find any <quote>emergent property</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:732 +msgid "" +"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 <emphasis>test</emphasis> is something that <emphasis>you</" +"emphasis> run when <command role=\"hg-cmd\">hg bisect</command> chooses a " +"changeset. A <emphasis>probe</emphasis> is what <command role=\"hg-cmd\">hg " +"bisect</command> runs to tell whether a revision is good. Finally, we'll use " +"the word <quote>bisect</quote>, as both a noun and a verb, to stand in for " +"the phrase <quote>search using the <command role=\"hg-cmd\">hg bisect</" +"command> command</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:745 +msgid "" +"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 <emphasis>days</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:755 +msgid "" +"What the <command role=\"hg-cmd\">hg bisect</command> command does is use its " +"knowledge of the <quote>shape</quote> of your project's revision history to " +"perform a search in time proportional to the <emphasis>logarithm</emphasis> " +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch08-undo.xml:766 +msgid "" +"The <command role=\"hg-cmd\">hg bisect</command> command is aware of the " +"<quote>branchy</quote> nature of a Mercurial project's revision history, so " +"it has no problems dealing with branches, merges, or multiple heads in a " +"repository. It can prune entire branches of history with a single probe, " +"which is how it operates so efficiently." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:774 +msgid "Using the <command role=\"hg-cmd\">hg bisect</command> command" +msgstr "使用命令 <command role=\"hg-cmd\">hg bisect</command>" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:777 +msgid "" +"Here's an example of <command role=\"hg-cmd\">hg bisect</command> in action." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch08-undo.xml:781 +msgid "" +"In versions 0.9.5 and earlier of Mercurial, <command role=\"hg-cmd\">hg " +"bisect</command> was not a core command: it was distributed with Mercurial as " +"an extension. This section describes the built-in command, not the old " +"extension." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:788 +msgid "" +"Now let's create a repository, so that we can try out the <command role=\"hg-" +"cmd\">hg bisect</command> command in isolation." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:794 +msgid "" +"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 <quote>bug</quote>. This loop creates 35 changesets, each adding a " +"single file to the repository. We'll represent our <quote>bug</quote> with a " +"file that contains the text <quote>i have a gub</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:804 +msgid "" +"The next thing that we'd like to do is figure out how to use the <command " +"role=\"hg-cmd\">hg bisect</command> command. We can use Mercurial's normal " +"built-in help mechanism for this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:811 +msgid "" +"The <command role=\"hg-cmd\">hg bisect</command> command works in steps. " +"Each step proceeds as follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:814 +msgid "You run your binary test." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:816 +msgid "" +"If the test succeeded, you tell <command role=\"hg-cmd\">hg bisect</command> " +"by running the <command role=\"hg-cmd\">hg bisect good</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:821 +msgid "" +"If it failed, run the <command role=\"hg-cmd\">hg bisect bad</command> " +"command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:825 +msgid "" +"The command uses your information to decide which changeset to test next." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> +#: ../en/ch08-undo.xml:828 +msgid "" +"It updates the working directory to that changeset, and the process begins " +"again." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:831 +msgid "" +"The process ends when <command role=\"hg-cmd\">hg bisect</command> identifies " +"a unique changeset that marks the point where your test transitioned from " +"<quote>succeeding</quote> to <quote>failing</quote>." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:836 +msgid "" +"To start the search, we must run the <command role=\"hg-cmd\">hg bisect --" +"reset</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:841 +msgid "" +"In our case, the binary test we use is simple: we check to see if any file in " +"the repository contains the string <quote>i have a gub</quote>. If it does, " +"this changeset contains the change that <quote>caused the bug</quote>. By " +"convention, a changeset that has the property we're searching for is " +"<quote>bad</quote>, while one that doesn't is <quote>good</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:849 +msgid "" +"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 <quote>bad</quote>." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:856 +msgid "" +"Our next task is to nominate a changeset that we know <emphasis>doesn't</" +"emphasis> have the bug; the <command role=\"hg-cmd\">hg bisect</command> " +"command will <quote>bracket</quote> 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 <quote>good</quote> " +"changeset later.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:866 +msgid "Notice that this command printed some output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:868 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:872 +msgid "" +"It updated the working directory to the next changeset to test, and told us " +"which changeset it's testing." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:877 +msgid "" +"We now run our test in the working directory. We use the <command>grep</" +"command> command to see if our <quote>bad</quote> file is present in the " +"working directory. If it is, this revision is bad; if not, this revision is " +"good. &interaction.bisect.search.step1;" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:883 +msgid "" +"This test looks like a perfect candidate for automation, so let's turn it " +"into a shell function." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:887 +msgid "" +"We can now run an entire test step with a single command, <literal>mytest</" +"literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:892 +msgid "A few more invocations of our canned test step command, and we're done." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:897 +msgid "" +"Even though we had 40 changesets to search through, the <command role=\"hg-cmd" +"\">hg bisect</command> command let us find the changeset that introduced our " +"<quote>bug</quote> with only five tests. Because the number of tests that " +"the <command role=\"hg-cmd\">hg bisect</command> command performs grows " +"logarithmically with the number of changesets to search, the advantage that " +"it has over the <quote>brute force</quote> search approach increases with " +"every changeset you add." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:908 +msgid "Cleaning up after your search" +msgstr "搜索后的清理" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:910 +msgid "" +"When you're finished using the <command role=\"hg-cmd\">hg bisect</command> " +"command in a repository, you can use the <command role=\"hg-cmd\">hg bisect " +"reset</command> 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, <command role=\"hg-cmd\">hg bisect</" +"command> won't let you start a new search in that repository until you do a " +"<command role=\"hg-cmd\">hg bisect reset</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch08-undo.xml:925 +msgid "Tips for finding bugs effectively" +msgstr "有效查找问题的技巧" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:928 +msgid "Give consistent input" +msgstr "给出一致的输入" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:930 +msgid "" +"The <command role=\"hg-cmd\">hg bisect</command> 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 <emphasis>might</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:942 +msgid "Automate as much as possible" +msgstr "尽量自动" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:944 +msgid "" +"When I started using the <command role=\"hg-cmd\">hg bisect</command> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:952 +msgid "" +"My initial problems with driving the <command role=\"hg-cmd\">hg bisect</" +"command> 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 <command role=\"hg-cmd\">hg bisect</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:961 +msgid "The key to automated testing is twofold:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:963 +msgid "always test for the same symptom, and" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:965 +msgid "" +"always feed consistent input to the <command role=\"hg-cmd\">hg bisect</" +"command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:968 +msgid "" +"In my tutorial example above, the <command>grep</command> command tests for " +"the symptom, and the <literal>if</literal> statement takes the result of this " +"check and ensures that we always feed the same input to the <command role=" +"\"hg-cmd\">hg bisect</command> command. The <literal>mytest</literal> " +"function marries these together in a reproducible way, so that every test is " +"uniform and consistent." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:978 +msgid "Check your results" +msgstr "检查你的结果" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:980 +msgid "" +"Because the output of a <command role=\"hg-cmd\">hg bisect</command> 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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:986 +msgid "" +"The changeset that it reports as the first bad revision. Your test should " +"still report this as bad." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:990 +msgid "" +"The parent of that changeset (either parent, if it's a merge). Your test " +"should report this changeset as good." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch08-undo.xml:994 +msgid "" +"A child of that changeset. Your test should report this changeset as bad." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:1000 +msgid "Beware interference between bugs" +msgstr "谨防问题之间的冲突" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1002 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1010 +msgid "" +"It is possible that this other bug completely <quote>masks</quote> 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 <command role=\"hg-cmd\">hg bisect</command> " +"command cannot help you directly. Instead, you can mark a changeset as " +"untested by running <command role=\"hg-cmd\">hg bisect --skip</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1020 +msgid "" +"A different problem could arise if your test for a bug's presence is not " +"specific enough. If you check for <quote>my program crashes</quote>, then " +"both your crashing bug and an unrelated crashing bug that masks it will look " +"like the same thing, and mislead <command role=\"hg-cmd\">hg bisect</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1027 +msgid "" +"Another useful situation in which to use <command role=\"hg-cmd\">hg bisect --" +"skip</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch08-undo.xml:1036 +msgid "Bracket your search lazily" +msgstr "减少你的查找工作" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1038 +msgid "" +"Choosing the first <quote>good</quote> and <quote>bad</quote> changesets that " +"will mark the end points of your search is often easy, but it bears a little " +"discussion nevertheless. From the perspective of <command role=\"hg-cmd\">hg " +"bisect</command>, the <quote>newest</quote> changeset is conventionally " +"<quote>bad</quote>, and the older changeset is <quote>good</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1046 +msgid "" +"If you're having trouble remembering when a suitable <quote>good</quote> " +"change was, so that you can tell <command role=\"hg-cmd\">hg bisect</" +"command>, 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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch08-undo.xml:1055 +msgid "" +"Even if you end up <quote>early</quote> by thousands of changesets or months " +"of history, you will only add a handful of tests to the total number that " +"<command role=\"hg-cmd\">hg bisect</command> must perform, thanks to its " +"logarithmic behaviour." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch09-hook.xml:5 +msgid "Handling repository events with hooks" +msgstr "使用钩子处理版本库事件" + +#. type: Content of: <book><chapter><para> +#: ../en/ch09-hook.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch09-hook.xml:12 +msgid "" +"The name Mercurial uses for one of these actions is a <emphasis>hook</" +"emphasis>. Hooks are called <quote>triggers</quote> in some revision control " +"systems, but the two names refer to the same idea." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:18 +msgid "An overview of hooks in Mercurial" +msgstr "Mercurial 钩子概述" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:20 +msgid "" +"Here is a brief list of the hooks that Mercurial supports. We will revisit " +"each of these hooks in more detail later, in <xref linkend=\"sec:hook:ref\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:25 +msgid "" +"<literal role=\"hook\">changegroup</literal>: This is run after a group of " +"changesets has been brought into the repository from elsewhere." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:29 +msgid "" +"<literal role=\"hook\">commit</literal>: This is run after a new changeset " +"has been created in the local repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:33 +msgid "" +"<literal role=\"hook\">incoming</literal>: This is run once for each new " +"changeset that is brought into the repository from elsewhere. Notice the " +"difference from <literal role=\"hook\">changegroup</literal>, which is run " +"once per <emphasis>group</emphasis> of changesets brought in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:40 +msgid "" +"<literal role=\"hook\">outgoing</literal>: This is run after a group of " +"changesets has been transmitted from this repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:44 +msgid "" +"<literal role=\"hook\">prechangegroup</literal>: This is run before starting " +"to bring a group of changesets into the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:49 +msgid "" +"<literal role=\"hook\">precommit</literal>: Controlling. This is run before " +"starting a commit." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:53 +msgid "" +"<literal role=\"hook\">preoutgoing</literal>: Controlling. This is run before " +"starting to transmit a group of changesets from this repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:58 +msgid "" +"<literal role=\"hook\">pretag</literal>: Controlling. This is run before " +"creating a tag." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:62 +msgid "" +"<literal role=\"hook\">pretxnchangegroup</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:70 +msgid "" +"<literal role=\"hook\">pretxncommit</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:76 +msgid "" +"<literal role=\"hook\">preupdate</literal>: Controlling. This is run before " +"starting an update or merge of the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:81 +msgid "" +"<literal role=\"hook\">tag</literal>: This is run after a tag is created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:85 +msgid "" +"<literal role=\"hook\">update</literal>: This is run after an update or merge " +"of the working directory has finished." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:90 +msgid "" +"Each of the hooks whose description begins with the word <quote>Controlling</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:99 +msgid "Hooks and security" +msgstr "钩子与安全性" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:102 +msgid "Hooks are run with your privileges" +msgstr "钩子以你的特权执行" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:104 +msgid "" +"When you run a Mercurial command in a repository, and the command causes a " +"hook to run, that hook runs on <emphasis>your</emphasis> system, under " +"<emphasis>your</emphasis> user account, with <emphasis>your</emphasis> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:115 +msgid "" +"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 <filename role=\"special\">~/.hgrc</" +"filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:122 +msgid "" +"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 " +"<quote>you</quote>. For example, if you <command role=\"hg-cmd\">hg pull</" +"command> from that repository, and its <filename role=\"special\">.hg/hgrc</" +"filename> defines a local <literal role=\"hook\">outgoing</literal> hook, " +"that hook will run under your user account, even though you don't own that " +"repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch09-hook.xml:134 +msgid "" +"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 <literal role=\"hook" +"\">outgoing</literal> hook will run under whatever account is executing the " +"server process, on the server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:142 +msgid "" +"XXX To see what hooks are defined in a repository, use the <command role=\"hg-" +"cmd\">hg config hooks</command> command. If you are working in one " +"repository, but talking to another that you do not own (e.g. using <command " +"role=\"hg-cmd\">hg pull</command> or <command role=\"hg-cmd\">hg incoming</" +"command>), remember that it is the other repository's hooks you should be " +"checking, not your own." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:153 +msgid "Hooks do not propagate" +msgstr "钩子不会传播" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:155 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:162 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:168 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:175 +msgid "" +"In a corporate intranet, this is somewhat easier to control, as you can for " +"example provide a <quote>standard</quote> installation of Mercurial on an NFS " +"filesystem, and use a site-wide <filename role=\"special\">~/.hgrc</filename> " +"file to define hooks that all users will see. However, this too has its " +"limits; see below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:184 +msgid "Hooks can be overridden" +msgstr "钩子可以被覆盖" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:186 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:191 +msgid "" +"If you deploy a system- or site-wide <filename role=\"special\">~/.hgrc</" +"filename> file that defines some hooks, you should thus understand that your " +"users can disable or override those hooks." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:199 +msgid "Ensuring that critical hooks are run" +msgstr "确保关键钩子的执行" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:201 +msgid "" +"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 <filename role=\"special\">~/.hgrc</filename> won't work " +"for remote users on laptops, and of course local users can subvert it at will " +"by overriding the hook." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:210 +msgid "" +"Instead, you can set up your policies for use of Mercurial so that people are " +"expected to propagate changes through a well-known <quote>canonical</quote> " +"server that you have locked down and configured appropriately." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:216 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:225 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:236 +msgid "Care with <literal>pretxn</literal> hooks in a shared-access repository" +msgstr "在共享版本库中注意 <literal>pretxn</literal> 钩子" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:239 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:244 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:251 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:259 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:266 +msgid "" +"When Mercurial <emphasis>writes</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:277 +msgid "" +"When Mercurial <emphasis>reads</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:283 +msgid "" +"Some controlling hooks (<literal role=\"hook\">pretxncommit</literal> and " +"<literal role=\"hook\">pretxnchangegroup</literal>) 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:291 +msgid "" +"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 <quote>really there</quote>. The longer the hook " +"runs, the longer that window is open." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:299 +msgid "The problem illustrated" +msgstr "问题的演示" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:301 +msgid "" +"In principle, a good use for the <literal role=\"hook\">pretxnchangegroup</" +"literal> 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 <quote>break " +"the build</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:312 +msgid "" +"The safest technological answer to this challenge is to set up such a " +"<quote>gatekeeper</quote> repository as <emphasis>unidirectional</emphasis>. " +"Let it take changes pushed in from the outside, but do not allow anyone to " +"pull changes from it (use the <literal role=\"hook\">preoutgoing</literal> " +"hook to lock it down). Configure a <literal role=\"hook\">changegroup</" +"literal> hook so that if a build or test succeeds, the hook will push the new " +"changes out to another repository that people <emphasis>can</emphasis> pull " +"from." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:324 +msgid "" +"In practice, putting a centralised bottleneck like this in place is not often " +"a good idea, and transaction visibility has nothing to do with the problem. " +"As the size of a project&emdash;and the time it takes to build and " +"test&emdash;grows, you rapidly run into a wall with this <quote>try before " +"you buy</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:335 +msgid "" +"An approach that scales better is to get people to build and test before they " +"push, then run automated builds and tests centrally <emphasis>after</" +"emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:346 +msgid "A short tutorial on using hooks" +msgstr "使用钩子的简短指南" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:348 +msgid "" +"It is easy to write a Mercurial hook. Let's start with a hook that runs when " +"you finish a <command role=\"hg-cmd\">hg commit</command>, and simply prints " +"the hash of the changeset you just created. The hook is called <literal role=" +"\"hook\">commit</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:355 +msgid "All hooks follow the pattern in this example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:359 +msgid "" +"You add an entry to the <literal role=\"rc-hooks\">hooks</literal> section of " +"your <filename role=\"special\">~/.hgrc</filename>. 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</envar> in the example)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:369 +msgid "Performing multiple actions per event" +msgstr "每个事件执行多个操作" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:371 +msgid "" +"Quite often, you will want to define more than one hook for a particular kind " +"of event, as shown below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:376 +msgid "" +"Mercurial lets you do this by adding an <emphasis>extension</emphasis> 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 <quote><literal>.</literal></quote> " +"character), followed by some more text of your choosing. For example, " +"Mercurial will run both <literal>commit.foo</literal> and <literal>commit." +"bar</literal> when the <literal>commit</literal> event occurs." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:387 +msgid "" +"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 " +"<literal>commit.bar</literal> before <literal>commit.foo</literal>, and " +"<literal>commit</literal> before both." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:396 +msgid "" +"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 <xref linkend=\"sec:hook:perm\"/> for an example)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:407 +msgid "Controlling whether an activity can proceed" +msgstr "控制处理的活动" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:409 +msgid "" +"In our earlier examples, we used the <literal role=\"hook\">commit</literal> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:416 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:422 +msgid "" +"The <literal role=\"hook\">pretxncommit</literal> 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 <literal role=\"hook\">pretxncommit</literal> hook " +"has the ability to decide whether the transaction can complete, or must be " +"rolled back." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:431 +msgid "" +"If the <literal role=\"hook\">pretxncommit</literal> hook exits with a status " +"code of zero, the transaction is allowed to complete; the commit finishes; " +"and the <literal role=\"hook\">commit</literal> hook is run. If the <literal " +"role=\"hook\">pretxncommit</literal> hook exits with a non-zero status code, " +"the transaction is rolled back; the metadata representing the changeset is " +"erased; and the <literal role=\"hook\">commit</literal> hook is not run." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:443 +msgid "" +"The hook in the example above checks that a commit comment contains a bug " +"ID. If it does, the commit can complete. If not, the commit is rolled back." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:451 +msgid "Writing your own hooks" +msgstr "编写钩子" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:453 +msgid "" +"When you are writing a hook, you might find it useful to run Mercurial either " +"with the <option role=\"hg-opt-global\">-v</option> option, or the <envar " +"role=\"rc-item-ui\">verbose</envar> config item set to <quote>true</quote>. " +"When you do so, Mercurial will print a message before it calls each hook." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:462 +msgid "Choosing how your hook should run" +msgstr "选择钩子的执行方式" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:464 +msgid "" +"You can write a hook either as a normal program&emdash;typically a shell " +"script&emdash;or as a Python function that is executed within the Mercurial " +"process." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:469 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:476 +msgid "" +"An in-process Python hook has complete access to the Mercurial API, and does " +"not <quote>shell out</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:484 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:493 +msgid "Hook parameters" +msgstr "钩子的参数" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:495 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:501 +msgid "" +"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 " +"<quote>true</quote>) or 0 (for <quote>false</quote>) as an environment " +"variable for an external hook. If a hook parameter is named <literal>foo</" +"literal>, the keyword argument for a Python hook will also be named " +"<literal>foo</literal>, while the environment variable for an external hook " +"will be named <literal>HG_FOO</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:515 +msgid "Hook return values and activity control" +msgstr "钩子的返回值与活动控制" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:517 +msgid "" +"A hook that executes successfully must exit with a status of zero if " +"external, or return boolean <quote>false</quote> if in-process. Failure is " +"indicated with a non-zero exit status from an external hook, or an in-process " +"hook returning boolean <quote>true</quote>. If an in-process hook raises an " +"exception, the hook is considered to have failed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:525 +msgid "" +"For a hook that controls whether an activity can proceed, zero/false means " +"<quote>allow</quote>, while non-zero/true/exception means <quote>deny</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:532 +msgid "Writing an external hook" +msgstr "编写外部钩子" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:534 +msgid "" +"When you define an external hook in your <filename role=\"special\">~/.hgrc</" +"filename> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:541 +msgid "" +"An executable hook is always run with its current directory set to a " +"repository's root directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:545 +msgid "" +"Each hook parameter is passed in as an environment variable; the name is " +"upper-cased, and prefixed with the string <quote><literal>HG_</literal></" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:550 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:561 +msgid "Telling Mercurial to use an in-process hook" +msgstr "让 Mercurial 使用进程内钩子" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:563 +msgid "" +"The <filename role=\"special\">~/.hgrc</filename> 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 <quote><literal>python:</literal></quote>, " +"and continue with the fully-qualified name of a callable object to use as the " +"hook's value." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:571 +msgid "" +"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</envar> " +"right, it should <quote>just work</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:577 +msgid "" +"The following <filename role=\"special\">~/.hgrc</filename> example snippet " +"illustrates the syntax and meaning of the notions we just described." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:583 +msgid "" +"When Mercurial runs the <literal>commit.example</literal> hook, it imports " +"<literal>mymodule.submodule</literal>, looks for the callable object named " +"<literal>myhook</literal>, and calls it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:591 +msgid "Writing an in-process hook" +msgstr "编写进程内钩子" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:593 +msgid "" +"The simplest in-process hook does nothing, but illustrates the basic shape of " +"the hook API:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:598 +msgid "" +"The first argument to a Python hook is always a <literal role=\"py-mod-" +"mercurial.ui\">ui</literal> object. The second is a repository object; at " +"the moment, it is always an instance of <literal role=\"py-mod-mercurial." +"localrepo\">localrepository</literal>. 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 <literal>**kwargs</literal> above." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:613 +msgid "Some hook examples" +msgstr "钩子样例" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:616 +msgid "Writing meaningful commit messages" +msgstr "编写有意义的提交日志" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:618 +msgid "" +"It's hard to imagine a useful commit message being very short. The simple " +"<literal role=\"hook\">pretxncommit</literal> hook of the example below will " +"prevent you from committing a changeset with a message that is less than ten " +"bytes long." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:628 +msgid "Checking for trailing whitespace" +msgstr "检查行尾空格" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:630 +msgid "" +"An interesting use of a commit-related hook is to help you to write cleaner " +"code. A simple example of <quote>cleaner code</quote> is the dictum that a " +"change should not add any new lines of text that contain <quote>trailing " +"whitespace</quote>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:641 +msgid "" +"You can use either the <literal role=\"hook\">precommit</literal> or <literal " +"role=\"hook\">pretxncommit</literal> hook to tell whether you have a trailing " +"whitespace problem. If you use the <literal role=\"hook\">precommit</" +"literal> 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</" +"filename>, but the file <filename>bar</filename> contains trailing " +"whitespace, doing a check in the <literal role=\"hook\">precommit</literal> " +"hook will prevent you from committing <filename>foo</filename> due to the " +"problem with <filename>bar</filename>. This doesn't seem right." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:657 +msgid "" +"Should you choose the <literal role=\"hook\">pretxncommit</literal> 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 " +"<command role=\"hg-cmd\">hg commit</command> again." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:670 +msgid "" +"In this example, we introduce a simple <literal role=\"hook\">pretxncommit</" +"literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:681 +msgid "" +"The above version 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 the <option role=\"hg-opt-commit\">-l " +"filename</option> option to <command role=\"hg-cmd\">hg commit</command> to " +"reuse the saved commit message once you've corrected the problem." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:695 +msgid "" +"As a final aside, note in the example above the use of <command>perl</" +"command>'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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:705 +msgid "Bundled hooks" +msgstr "内置的钩子" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch09-hook.xml:707 +msgid "" +"Mercurial ships with several bundled hooks. You can find them in the " +"<filename class=\"directory\">hgext</filename> directory of a Mercurial " +"source tree. If you are using a Mercurial binary package, the hooks will be " +"located in the <filename class=\"directory\">hgext</filename> directory of " +"wherever your package installer put Mercurial." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:716 +msgid "" +"<literal role=\"hg-ext\">acl</literal>&emdash;access control for parts of a " +"repository" +msgstr "<literal role=\"hg-ext\">acl</literal>—版本库的访问控制" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:719 +msgid "" +"The <literal role=\"hg-ext\">acl</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:727 +msgid "" +"This extension implements access control based on the identity of the user " +"performing a push, <emphasis>not</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:737 +msgid "Configuring the <literal role=\"hook\">acl</literal> hook" +msgstr "配置 <literal role=\"hook\">acl</literal> 钩子" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:740 +msgid "" +"In order to manage incoming changesets, the <literal role=\"hg-ext\">acl</" +"literal> hook must be used as a <literal role=\"hook\">pretxnchangegroup</" +"literal> hook. This lets it see which files are modified by each incoming " +"changeset, and roll back a group of changesets if they modify " +"<quote>forbidden</quote> files. Example:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:750 +msgid "" +"The <literal role=\"hg-ext\">acl</literal> extension is configured using " +"three sections." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:754 +msgid "" +"The <literal role=\"rc-acl\">acl</literal> section has only one entry, <envar " +"role=\"rc-item-acl\">sources</envar>, which lists the sources of incoming " +"changesets that the hook should pay attention to. You don't normally need to " +"configure this section." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:761 +msgid "" +"<envar role=\"rc-item-acl\">serve</envar>: Control incoming changesets that " +"are arriving from a remote repository over http or ssh. This is the default " +"value of <envar role=\"rc-item-acl\">sources</envar>, and usually the only " +"setting you'll need for this configuration item." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:769 +msgid "" +"<envar role=\"rc-item-acl\">pull</envar>: Control incoming changesets that " +"are arriving via a pull from a local repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:774 +msgid "" +"<envar role=\"rc-item-acl\">push</envar>: Control incoming changesets that " +"are arriving via a push from a local repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:779 +msgid "" +"<envar role=\"rc-item-acl\">bundle</envar>: Control incoming changesets that " +"are arriving from another repository via a bundle." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:785 +msgid "" +"The <literal role=\"rc-acl.allow\">acl.allow</literal> 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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:794 +msgid "" +"The <literal role=\"rc-acl.deny\">acl.deny</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:800 +msgid "" +"The syntaxes for the <literal role=\"rc-acl.allow\">acl.allow</literal> and " +"<literal role=\"rc-acl.deny\">acl.deny</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:808 +msgid "" +"In the following example, the user <literal>docwriter</literal> can only push " +"changes to the <filename class=\"directory\">docs</filename> subtree of the " +"repository, while <literal>intern</literal> can push changes to any file or " +"directory except <filename class=\"directory\">source/sensitive</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:822 ../en/ch09-hook.xml:1089 ../en/ch09-hook.xml:1279 +msgid "Testing and troubleshooting" +msgstr "测试与问题处理" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:824 +msgid "" +"If you want to test the <literal role=\"hg-ext\">acl</literal> hook, run it " +"with Mercurial's debugging output enabled. Since you'll probably be running " +"it on a server where it's not convenient (or sometimes possible) to pass in " +"the <option role=\"hg-opt-global\">--debug</option> option, don't forget that " +"you can enable debugging output in your <filename role=\"special\">~/.hgrc</" +"filename>:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:835 +msgid "" +"With this enabled, the <literal role=\"hg-ext\">acl</literal> hook will print " +"enough information to let you figure out why it is allowing or forbidding " +"pushes from specific users." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:844 +msgid "" +"<literal role=\"hg-ext\">bugzilla</literal>&emdash;integration with Bugzilla" +msgstr "<literal role=\"hg-ext\">bugzilla</literal>—与 Bugzilla 的集成" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:848 +msgid "" +"The <literal role=\"hg-ext\">bugzilla</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:855 +msgid "" +"It adds a comment to the bug that looks like this (you can configure the " +"contents of the comment&emdash;see below):" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:864 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:871 +msgid "" +"You can use the code in this hook as a starting point for some more exotic " +"Bugzilla integration recipes. Here are a few possibilities:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:876 +msgid "" +"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 <literal " +"role=\"hook\">pretxncommit</literal> hook. This would allow the hook to " +"reject changes that didn't contain bug IDs." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:884 +msgid "" +"Allow incoming changesets to automatically modify the <emphasis>state</" +"emphasis> of a bug, as well as simply adding a comment. For example, the " +"hook could recognise the string <quote>fixed bug 31337</quote> as indicating " +"that it should update the state of bug 31337 to <quote>requires testing</" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:894 +msgid "Configuring the <literal role=\"hook\">bugzilla</literal> hook" +msgstr "配置 <literal role=\"hook\">bugzilla</literal> 钩子" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:897 +msgid "" +"You should configure this hook in your server's <filename role=\"special\">~/." +"hgrc</filename> as an <literal role=\"hook\">incoming</literal> hook, for " +"example as follows:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:905 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:911 +msgid "" +"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 <citation>web:mysql-python</" +"citation>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:918 +msgid "" +"Configuration information for this hook lives in the <literal role=\"rc-" +"bugzilla\">bugzilla</literal> section of your <filename role=\"special\">~/." +"hgrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:923 +msgid "" +"<envar role=\"rc-item-bugzilla\">version</envar>: 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 <literal>2.16</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:932 +msgid "" +"<envar role=\"rc-item-bugzilla\">host</envar>: 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 <literal role=\"hook" +"\">bugzilla</literal> hook on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:939 +msgid "" +"<envar role=\"rc-item-bugzilla\">user</envar>: 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 <literal role=\"hook" +"\">bugzilla</literal> hook on. This user must be able to access and modify " +"Bugzilla tables. The default value of this item is <literal>bugs</literal>, " +"which is the standard name of the Bugzilla user in a MySQL database." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:950 +msgid "" +"<envar role=\"rc-item-bugzilla\">password</envar>: 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 <filename role=\"special\">~/." +"hgrc</filename> file where you store this information." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:959 +msgid "" +"<envar role=\"rc-item-bugzilla\">db</envar>: The name of the Bugzilla " +"database on the MySQL server. The default value of this item is " +"<literal>bugs</literal>, which is the standard name of the MySQL database " +"where Bugzilla stores its data." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:966 +msgid "" +"<envar role=\"rc-item-bugzilla\">notify</envar>: 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 <filename class=\"directory\">/var/www/html/bugzilla</filename>:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:979 +msgid "" +"The Bugzilla <literal>processmail</literal> program expects to be given a bug " +"ID (the hook replaces <quote><literal>%s</literal></quote> 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 <literal>processmail</" +"literal> on the server where Bugzilla is installed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:994 +msgid "Mapping committer names to Bugzilla user names" +msgstr "提交者的名称与 Bugzilla 用户名称的映射" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:996 +msgid "" +"By default, the <literal role=\"hg-ext\">bugzilla</literal> 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 <literal role=\"rc-" +"usermap\">usermap</literal> section." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1005 +msgid "" +"Each item in the <literal role=\"rc-usermap\">usermap</literal> section " +"contains an email address on the left, and a Bugzilla user name on the right." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1012 +msgid "" +"You can either keep the <literal role=\"rc-usermap\">usermap</literal> data " +"in a normal <filename role=\"special\">~/.hgrc</filename>, or tell the " +"<literal role=\"hg-ext\">bugzilla</literal> hook to read the information from " +"an external <filename>usermap</filename> file. In the latter case, you can " +"store <filename>usermap</filename> data by itself in (for example) a user-" +"modifiable repository. This makes it possible to let your users maintain " +"their own <envar role=\"rc-item-bugzilla\">usermap</envar> entries. The main " +"<filename role=\"special\">~/.hgrc</filename> file might look like this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1028 +msgid "" +"While the <filename>usermap</filename> file that it refers to might look like " +"this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:1036 +msgid "Configuring the text that gets added to a bug" +msgstr "配置增加到问题中的正文" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1038 +msgid "" +"You can configure the text that this hook adds as a comment; you specify it " +"in the form of a Mercurial template. Several <filename role=\"special\">~/." +"hgrc</filename> entries (still in the <literal role=\"rc-bugzilla\">bugzilla</" +"literal> section) control this behaviour." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1045 +msgid "" +"<literal>strip</literal>: 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 <filename class=\"directory\">/" +"home/hg/repos</filename>, and you have a repository whose path is <filename " +"class=\"directory\">/home/hg/repos/app/tests</filename>, then setting " +"<literal>strip</literal> to <literal>4</literal> will give a partial path of " +"<filename class=\"directory\">app/tests</filename>. The hook will make this " +"partial path available when expanding a template, as <literal>webroot</" +"literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1059 +msgid "" +"<literal>template</literal>: The text of the template to use. In addition to " +"the usual changeset-related variables, this template can use <literal>hgweb</" +"literal> (the value of the <literal>hgweb</literal> configuration item above) " +"and <literal>webroot</literal> (the path constructed using <literal>strip</" +"literal> above)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1069 +msgid "" +"In addition, you can add a <envar role=\"rc-item-web\">baseurl</envar> item " +"to the <literal role=\"rc-web\">web</literal> section of your <filename role=" +"\"special\">~/.hgrc</filename>. The <literal role=\"hg-ext\">bugzilla</" +"literal> 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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1081 +msgid "" +"Here is an example set of <literal role=\"hg-ext\">bugzilla</literal> hook " +"config information." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1091 +msgid "" +"The most common problems with configuring the <literal role=\"hg-ext" +"\">bugzilla</literal> hook relate to running Bugzilla's " +"<filename>processmail</filename> script and mapping committer names to user " +"names." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1097 +msgid "" +"Recall from <xref linkend=\"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</filename> script. The <filename>processmail</" +"filename> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1108 +msgid "" +"You can cause <filename>processmail</filename> to be run with the suitable " +"user's identity using the <command>sudo</command> command. Here is an " +"example entry for a <filename>sudoers</filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1115 +msgid "" +"This allows the <literal>hg_user</literal> user to run a " +"<filename>processmail-wrapper</filename> program under the identity of " +"<literal>httpd_user</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1120 +msgid "" +"This indirection through a wrapper script is necessary, because " +"<filename>processmail</filename> 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</filename> file. The contents of the " +"wrapper script are simple:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1129 +msgid "" +"It doesn't seem to matter what email address you pass to " +"<filename>processmail</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1133 +msgid "" +"If your <literal role=\"rc-usermap\">usermap</literal> is not set up " +"correctly, users will see an error message from the <literal role=\"hg-ext" +"\">bugzilla</literal> hook when they push changes to the server. The error " +"message will look like this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1140 +msgid "" +"What this means is that the committer's address, <literal>john.q." +"public@example.com</literal>, is not a valid Bugzilla user name, nor does it " +"have an entry in your <literal role=\"rc-usermap\">usermap</literal> that " +"maps it to a valid Bugzilla user name." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1150 +msgid "" +"<literal role=\"hg-ext\">notify</literal>&emdash;send email notifications" +msgstr "<literal role=\"hg-ext\">notify</literal>—邮件通知" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1153 +msgid "" +"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 <literal role=\"hg-ext\">notify</literal> hook lets you send out " +"notifications to a set of email addresses whenever changesets arrive that " +"those subscribers are interested in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1161 +msgid "" +"As with the <literal role=\"hg-ext\">bugzilla</literal> hook, the <literal " +"role=\"hg-ext\">notify</literal> hook is template-driven, so you can " +"customise the contents of the notification messages that it sends." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1167 +msgid "" +"By default, the <literal role=\"hg-ext\">notify</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:1175 +msgid "Configuring the <literal role=\"hg-ext\">notify</literal> hook" +msgstr "配置 <literal role=\"hg-ext\">notify</literal> 钩子" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1178 +msgid "" +"You can set up the <literal role=\"hg-ext\">notify</literal> 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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1190 +msgid "" +"Configuration information for this hook lives in the <literal role=\"rc-notify" +"\">notify</literal> section of a <filename role=\"special\">~/.hgrc</" +"filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1195 +msgid "" +"<envar role=\"rc-item-notify\">test</envar>: By default, this hook does not " +"send out email at all; instead, it prints the message that it " +"<emphasis>would</emphasis> send. Set this item to <literal>false</literal> " +"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 " +"<quote>broken</quote> notifications while you debug your configuration." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1207 +msgid "" +"<envar role=\"rc-item-notify\">config</envar>: The path to a configuration " +"file that contains subscription information. This is kept separate from the " +"main <filename role=\"special\">~/.hgrc</filename> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1216 +msgid "" +"<envar role=\"rc-item-notify\">strip</envar>: 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 <filename class=\"directory\">/home/hg/repos</filename>, and " +"<literal role=\"hg-ext\">notify</literal> is considering a repository named " +"<filename class=\"directory\">/home/hg/repos/shared/test</filename>, setting " +"<envar role=\"rc-item-notify\">strip</envar> to <literal>4</literal> will " +"cause <literal role=\"hg-ext\">notify</literal> to trim the path it considers " +"down to <filename class=\"directory\">shared/test</filename>, and it will " +"match subscribers against that." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1233 +msgid "" +"<envar role=\"rc-item-notify\">template</envar>: The template text to use " +"when sending messages. This specifies both the contents of the message " +"header and its body." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1239 +msgid "" +"<envar role=\"rc-item-notify\">maxdiff</envar>: 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 " +"<literal>0</literal> to omit diffs from notification emails." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1248 +msgid "" +"<envar role=\"rc-item-notify\">sources</envar>: A list of sources of " +"changesets to consider. This lets you limit <literal role=\"hg-ext\">notify</" +"literal> to only sending out email about changes that remote users pushed " +"into this repository via a server, for example. See <xref linkend=\"sec:hook:" +"sources\"/> for the sources you can specify here." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1259 +msgid "" +"If you set the <envar role=\"rc-item-web\">baseurl</envar> item in the " +"<literal role=\"rc-web\">web</literal> section, you can use it in a template; " +"it will be available as <literal>webroot</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1265 +msgid "" +"Here is an example set of <literal role=\"hg-ext\">notify</literal> " +"configuration information." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1271 +msgid "This will produce a message that looks like the following:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1281 +msgid "" +"Do not forget that by default, the <literal role=\"hg-ext\">notify</literal> " +"extension <emphasis>will not send any mail</emphasis> until you explicitly " +"configure it to do so, by setting <envar role=\"rc-item-notify\">test</envar> " +"to <literal>false</literal>. Until you do that, it simply prints the message " +"it <emphasis>would</emphasis> send." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:1293 +msgid "Information for writers of hooks" +msgstr "编写钩子的信息" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1296 +msgid "In-process hook execution" +msgstr "进程内钩子的执行" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1298 +msgid "An in-process hook is called with arguments of the following form:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1302 +msgid "" +"The <literal>ui</literal> parameter is a <literal role=\"py-mod-mercurial.ui" +"\">ui</literal> object. The <literal>repo</literal> parameter is a <literal " +"role=\"py-mod-mercurial.localrepo\">localrepository</literal> object. The " +"names and values of the <literal>**kwargs</literal> parameters depend on the " +"hook being invoked, with the following common features:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1311 +msgid "" +"If a parameter is named <literal>node</literal> or <literal>parentN</" +"literal>, it will contain a hexadecimal changeset ID. The empty string is " +"used to represent <quote>null changeset ID</quote> instead of a string of " +"zeroes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1318 +msgid "" +"If a parameter is named <literal>url</literal>, it will contain the URL of a " +"remote repository, if that can be determined." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1323 +msgid "" +"Boolean-valued parameters are represented as Python <literal>bool</literal> " +"objects." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1328 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1335 +msgid "" +"If a hook returns a boolean <quote>false</quote> value, it is considered to " +"have succeeded. If it returns a boolean <quote>true</quote> value or raises " +"an exception, it is considered to have failed. A useful way to think of the " +"calling convention is <quote>tell me if you fail</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1342 +msgid "" +"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 <literal>bin</literal> function." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1350 +msgid "External hook execution" +msgstr "外部钩子的执行" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1352 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1360 +msgid "" +"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 <quote><literal>HG_</literal></quote>. For example, if the name of a " +"parameter is <quote><literal>node</literal></quote>, the name of the " +"environment variable representing that parameter will be " +"<quote><literal>HG_NODE</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1369 +msgid "" +"A boolean parameter is represented as the string <quote><literal>1</literal></" +"quote> for <quote>true</quote>, <quote><literal>0</literal></quote> for " +"<quote>false</quote>. If an environment variable is named <envar>HG_NODE</" +"envar>, <envar>HG_PARENT1</envar> or <envar>HG_PARENT2</envar>, it contains a " +"changeset ID represented as a hexadecimal string. The empty string is used " +"to represent <quote>null changeset ID</quote> instead of a string of zeroes. " +"If an environment variable is named <envar>HG_URL</envar>, it will contain " +"the URL of a remote repository, if that can be determined." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1381 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1388 +msgid "Finding out where changesets come from" +msgstr "检查修改集来自何处" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1390 +msgid "" +"A hook that involves the transfer of changesets between a local repository " +"and another may be able to find out information about the <quote>far side</" +"quote>. Mercurial knows <emphasis>how</emphasis> changes are being " +"transferred, and in many cases <emphasis>where</emphasis> they are being " +"transferred to or from." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:1399 +msgid "Sources of changesets" +msgstr "修改集的来源" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1401 +msgid "" +"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 <literal>source</literal>, or an environment variable named " +"<envar>HG_SOURCE</envar>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1409 +msgid "" +"<literal>serve</literal>: Changesets are transferred to or from a remote " +"repository over http or ssh." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1414 +msgid "" +"<literal>pull</literal>: Changesets are being transferred via a pull from one " +"repository into another." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1419 +msgid "" +"<literal>push</literal>: Changesets are being transferred via a push from one " +"repository into another." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1424 +msgid "" +"<literal>bundle</literal>: Changesets are being transferred to or from a " +"bundle." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><title> +#: ../en/ch09-hook.xml:1431 +msgid "Where changes are going&emdash;remote repository URLs" +msgstr "修改集要到哪里—远程版本库的地址" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1434 +msgid "" +"When possible, Mercurial will tell a hook the location of the <quote>far " +"side</quote> of an activity that transfers changeset data between " +"repositories. This is provided by Mercurial in a Python parameter named " +"<literal>url</literal>, or an environment variable named <envar>HG_URL</" +"envar>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1442 +msgid "" +"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:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1449 +msgid "" +"<literal>remote:ssh:1.2.3.4</literal>&emdash;remote ssh client, at the IP " +"address <literal>1.2.3.4</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1454 +msgid "" +"<literal>remote:http:1.2.3.4</literal>&emdash;remote http client, at the IP " +"address <literal>1.2.3.4</literal>. If the client is using SSL, this will be " +"of the form <literal>remote:https:1.2.3.4</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1461 +msgid "Empty&emdash;no information could be discovered about the remote client." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch09-hook.xml:1470 +msgid "Hook reference" +msgstr "钩子参考" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1473 +msgid "" +"<literal role=\"hook\">changegroup</literal>&emdash;after remote changesets " +"added" +msgstr "<literal role=\"hook\">changegroup</literal>—增加远程修改集之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1476 +msgid "" +"This hook is run after a group of pre-existing changesets has been added to " +"the repository, for example via a <command role=\"hg-cmd\">hg pull</command> " +"or <command role=\"hg-cmd\">hg unbundle</command>. This hook is run once per " +"operation that added one or more changesets. This is in contrast to the " +"<literal role=\"hook\">incoming</literal> hook, which is run once per " +"changeset, regardless of whether the changesets arrive in a group." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1486 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1492 ../en/ch09-hook.xml:1532 ../en/ch09-hook.xml:1576 +#: ../en/ch09-hook.xml:1618 ../en/ch09-hook.xml:1673 ../en/ch09-hook.xml:1713 +#: ../en/ch09-hook.xml:1749 ../en/ch09-hook.xml:1783 ../en/ch09-hook.xml:1846 +#: ../en/ch09-hook.xml:1904 ../en/ch09-hook.xml:1940 ../en/ch09-hook.xml:1967 +msgid "Parameters to this hook:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1495 ../en/ch09-hook.xml:1849 +msgid "" +"<literal>node</literal>: A changeset ID. The changeset ID of the first " +"changeset in the group that was added. All changesets between this and " +"<literal role=\"tag\">tip</literal>, inclusive, were added by a single " +"<command role=\"hg-cmd\">hg pull</command>, <command role=\"hg-cmd\">hg push</" +"command> or <command role=\"hg-cmd\">hg unbundle</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1504 ../en/ch09-hook.xml:1583 ../en/ch09-hook.xml:1676 +#: ../en/ch09-hook.xml:1859 +msgid "" +"<literal>source</literal>: A string. The source of these changes. See <xref " +"linkend=\"sec:hook:sources\"/> for details." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1509 ../en/ch09-hook.xml:1588 ../en/ch09-hook.xml:1639 +#: ../en/ch09-hook.xml:1681 ../en/ch09-hook.xml:1762 ../en/ch09-hook.xml:1864 +msgid "" +"<literal>url</literal>: A URL. The location of the remote repository, if " +"known. See <xref linkend=\"sec:hook:url\"/> for more information." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1515 +msgid "" +"See also: <literal role=\"hook\">incoming</literal> (<xref linkend=\"sec:hook:" +"incoming\"/>), <literal role=\"hook\">prechangegroup</literal> (<xref linkend=" +"\"sec:hook:prechangegroup\"/>), <literal role=\"hook\">pretxnchangegroup</" +"literal> (<xref linkend=\"sec:hook:pretxnchangegroup\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1526 +msgid "" +"<literal role=\"hook\">commit</literal>&emdash;after a new changeset is " +"created" +msgstr "<literal role=\"hook\">commit</literal>—创建新修改集之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1529 +msgid "This hook is run after a new changeset has been created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1535 ../en/ch09-hook.xml:1907 +msgid "" +"<literal>node</literal>: A changeset ID. The changeset ID of the newly " +"committed changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1539 ../en/ch09-hook.xml:1911 +msgid "" +"<literal>parent1</literal>: A changeset ID. The changeset ID of the first " +"parent of the newly committed changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1544 ../en/ch09-hook.xml:1916 +msgid "" +"<literal>parent2</literal>: A changeset ID. The changeset ID of the second " +"parent of the newly committed changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1550 +msgid "" +"See also: <literal role=\"hook\">precommit</literal> (<xref linkend=\"sec:" +"hook:precommit\"/>), <literal role=\"hook\">pretxncommit</literal> (<xref " +"linkend=\"sec:hook:pretxncommit\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1559 +msgid "" +"<literal role=\"hook\">incoming</literal>&emdash;after one remote changeset " +"is added" +msgstr "<literal role=\"hook\">incoming</literal>—增加远程修改集之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1562 +msgid "" +"This hook is run after a pre-existing changeset has been added to the " +"repository, for example via a <command role=\"hg-cmd\">hg push</command>. If " +"a group of changesets was added in a single operation, this hook is called " +"once for each added changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1569 +msgid "" +"You can use this hook for the same purposes as the <literal role=\"hook" +"\">changegroup</literal> hook (<xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1579 +msgid "" +"<literal>node</literal>: A changeset ID. The ID of the newly added changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1594 +msgid "" +"See also: <literal role=\"hook\">changegroup</literal> (<xref linkend=\"sec:" +"hook:changegroup\"/>) <literal role=\"hook\">prechangegroup</literal> (<xref " +"linkend=\"sec:hook:prechangegroup\"/>), <literal role=\"hook" +"\">pretxnchangegroup</literal> (<xref linkend=\"sec:hook:pretxnchangegroup\"/" +">)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1605 +msgid "" +"<literal role=\"hook\">outgoing</literal>&emdash;after changesets are " +"propagated" +msgstr "<literal role=\"hook\">outgoing</literal>—传播修改集之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1608 +msgid "" +"This hook is run after a group of changesets has been propagated out of this " +"repository, for example by a <command role=\"hg-cmd\">hg push</command> or " +"<command role=\"hg-cmd\">hg bundle</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1614 +msgid "" +"One possible use for this hook is to notify administrators that changes have " +"been pulled." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1621 +msgid "" +"<literal>node</literal>: A changeset ID. The changeset ID of the first " +"changeset of the group that was sent." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1626 +msgid "" +"<literal>source</literal>: A string. The source of the of the operation (see " +"<xref linkend=\"sec:hook:sources\"/>). If a remote client pulled changes " +"from this repository, <literal>source</literal> will be <literal>serve</" +"literal>. If the client that obtained changes from this repository was " +"local, <literal>source</literal> will be <literal>bundle</literal>, " +"<literal>pull</literal>, or <literal>push</literal>, depending on the " +"operation the client performed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1645 +msgid "" +"See also: <literal role=\"hook\">preoutgoing</literal> (<xref linkend=\"sec:" +"hook:preoutgoing\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1652 +msgid "" +"<literal role=\"hook\">prechangegroup</literal>&emdash;before starting to add " +"remote changesets" +msgstr "<literal role=\"hook\">prechangegroup</literal>—增加远程修改集之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1656 +msgid "" +"This controlling hook is run before Mercurial begins to add a group of " +"changesets from another repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1660 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1666 +msgid "" +"One use for this hook is to prevent external changes from being added to a " +"repository. For example, you could use this to <quote>freeze</quote> a " +"server-hosted branch temporarily or permanently so that users cannot push to " +"it, while still allowing a local administrator to modify the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1687 +msgid "" +"See also: <literal role=\"hook\">changegroup</literal> (<xref linkend=\"sec:" +"hook:changegroup\"/>), <literal role=\"hook\">incoming</literal> (<xref " +"linkend=\"sec:hook:incoming\"/>), <literal role=\"hook\">pretxnchangegroup</" +"literal> (<xref linkend=\"sec:hook:pretxnchangegroup\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1698 +msgid "" +"<literal role=\"hook\">precommit</literal>&emdash;before starting to commit a " +"changeset" +msgstr "<literal role=\"hook\">precommit</literal>—提交修改集之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1701 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1707 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1716 +msgid "" +"<literal>parent1</literal>: A changeset ID. The changeset ID of the first " +"parent of the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1721 +msgid "" +"<literal>parent2</literal>: A changeset ID. The changeset ID of the second " +"parent of the working directory." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1726 +msgid "" +"If the commit proceeds, the parents of the working directory will become the " +"parents of the new changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1730 +msgid "" +"See also: <literal role=\"hook\">commit</literal> (<xref linkend=\"sec:hook:" +"commit\"/>), <literal role=\"hook\">pretxncommit</literal> (<xref linkend=" +"\"sec:hook:pretxncommit\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1738 +msgid "" +"<literal role=\"hook\">preoutgoing</literal>&emdash;before starting to " +"propagate changesets" +msgstr "<literal role=\"hook\">preoutgoing</literal>—传播修改集之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1741 +msgid "" +"This hook is invoked before Mercurial knows the identities of the changesets " +"to be transmitted." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1745 +msgid "" +"One use for this hook is to prevent changes from being transmitted to another " +"repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1752 +msgid "" +"<literal>source</literal>: A string. The source of the operation that is " +"attempting to obtain changes from this repository (see <xref linkend=\"sec:" +"hook:sources\"/>). See the documentation for the <literal>source</literal> " +"parameter to the <literal role=\"hook\">outgoing</literal> hook, in <xref " +"linkend=\"sec:hook:outgoing\"/>, for possible values of this parameter." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1768 +msgid "" +"See also: <literal role=\"hook\">outgoing</literal> (<xref linkend=\"sec:hook:" +"outgoing\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1775 +msgid "" +"<literal role=\"hook\">pretag</literal>&emdash;before tagging a changeset" +msgstr "<literal role=\"hook\">pretag</literal>—创建标签之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1778 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1786 +msgid "" +"<literal>local</literal>: A boolean. Whether the tag is local to this " +"repository instance (i.e. stored in <filename role=\"special\">.hg/localtags</" +"filename>) or managed by Mercurial (stored in <filename role=\"special\">." +"hgtags</filename>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1793 +msgid "" +"<literal>node</literal>: A changeset ID. The ID of the changeset to be " +"tagged." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1797 +msgid "<literal>tag</literal>: A string. The name of the tag to be created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1802 +msgid "" +"If the tag to be created is revision-controlled, the <literal role=\"hook" +"\">precommit</literal> and <literal role=\"hook\">pretxncommit</literal> " +"hooks (<xref linkend=\"sec:hook:commit\"/> and <xref linkend=\"sec:hook:" +"pretxncommit\"/>) will also be run." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1810 +msgid "" +"See also: <literal role=\"hook\">tag</literal> (<xref linkend=\"sec:hook:tag" +"\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1815 +msgid "" +"<literal role=\"hook\">pretxnchangegroup</literal>&emdash;before completing " +"addition of remote changesets" +msgstr "" +"<literal role=\"hook\">pretxnchangegroup</literal>—完成增加远程修改集之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1819 +msgid "" +"This controlling hook is run before a transaction&emdash;that manages the " +"addition of a group of new changesets from outside the repository&emdash;" +"completes. If the hook succeeds, the transaction completes, and all of the " +"changesets become permanent within this repository. If the hook fails, the " +"transaction is rolled back, and the data for the changesets is erased." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1828 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1834 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1841 +msgid "" +"This hook can be used to automatically vet a group of changesets. If the " +"hook fails, all of the changesets are <quote>rejected</quote> when the " +"transaction rolls back." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1870 +msgid "" +"See also: <literal role=\"hook\">changegroup</literal> (<xref linkend=\"sec:" +"hook:changegroup\"/>), <literal role=\"hook\">incoming</literal> (<xref " +"linkend=\"sec:hook:incoming\"/>), <literal role=\"hook\">prechangegroup</" +"literal> (<xref linkend=\"sec:hook:prechangegroup\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1881 +msgid "" +"<literal role=\"hook\">pretxncommit</literal>&emdash;before completing commit " +"of new changeset" +msgstr "<literal role=\"hook\">pretxncommit</literal>—完成提交之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1884 +msgid "" +"This controlling hook is run before a transaction&emdash;that manages a new " +"commit&emdash;completes. If the hook succeeds, the transaction completes and " +"the changeset becomes permanent within this repository. If the hook fails, " +"the transaction is rolled back, and the commit data is erased." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1892 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1898 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1922 +msgid "" +"See also: <literal role=\"hook\">precommit</literal> (<xref linkend=\"sec:" +"hook:precommit\"/>)" +msgstr "" +"参见: <literal role=\"hook\">precommit</literal> (<xref linkend=\"sec:hook:" +"precommit\"/>)" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1929 +msgid "" +"<literal role=\"hook\">preupdate</literal>&emdash;before updating or merging " +"working directory" +msgstr "<literal role=\"hook\">preupdate</literal>—更新或合并工作目录之前" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1932 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1943 +msgid "" +"<literal>parent1</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1949 +msgid "" +"<literal>parent2</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1956 +msgid "" +"See also: <literal role=\"hook\">update</literal> (<xref linkend=\"sec:hook:" +"update\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1961 +msgid "<literal role=\"hook\">tag</literal>&emdash;after tagging a changeset" +msgstr "<literal role=\"hook\">tag</literal>—创建标签之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1964 +msgid "This hook is run after a tag has been created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1970 +msgid "" +"<literal>local</literal>: A boolean. Whether the new tag is local to this " +"repository instance (i.e. stored in <filename role=\"special\">.hg/" +"localtags</filename>) or managed by Mercurial (stored in <filename role=" +"\"special\">.hgtags</filename>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1978 +msgid "" +"<literal>node</literal>: A changeset ID. The ID of the changeset that was " +"tagged." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1982 +msgid "<literal>tag</literal>: A string. The name of the tag that was created." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1987 +msgid "" +"If the created tag is revision-controlled, the <literal role=\"hook\">commit</" +"literal> hook (section <xref linkend=\"sec:hook:commit\"/>) is run before " +"this hook." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:1992 +msgid "" +"See also: <literal role=\"hook\">pretag</literal> (<xref linkend=\"sec:hook:" +"pretag\"/>)" +msgstr "" +"参见: <literal role=\"hook\">pretag</literal> (<xref linkend=\"sec:hook:pretag" +"\"/>)" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1998 +msgid "" +"<literal role=\"hook\">update</literal>&emdash;after updating or merging " +"working directory" +msgstr "<literal role=\"hook\">update</literal>—更新或合并工作目录之后" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:2001 +msgid "" +"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> command fails to resolve conflicts in a file), this hook " +"communicates whether the update or merge completed cleanly." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:2009 +msgid "" +"<literal>error</literal>: A boolean. Indicates whether the update or merge " +"completed successfully." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:2014 +msgid "" +"<literal>parent1</literal>: 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:2020 +msgid "" +"<literal>parent2</literal>: A changeset ID. Only set if the working " +"directory was merged. The ID of the revision that the working directory was " +"merged with." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:2026 +msgid "" +"See also: <literal role=\"hook\">preupdate</literal> (<xref linkend=\"sec:" +"hook:preupdate\"/>)" +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch10-template.xml:5 +msgid "Customising the output of Mercurial" +msgstr "定制 Mercurial 的输出" + +#. type: Content of: <book><chapter><para> +#: ../en/ch10-template.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:14 +msgid "Using precanned output styles" +msgstr "使用预定义的输出样式" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:16 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:21 +msgid "" +"Before we take a look at Mercurial's bundled styles, let's review its normal " +"output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:26 +msgid "" +"This is somewhat informative, but it takes up a lot of space&emdash;five " +"lines of output per changeset. The <literal>compact</literal> style reduces " +"this to three lines, presented in a sparse manner." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:33 +msgid "" +"The <literal>changelog</literal> style hints at the expressive power of " +"Mercurial's templating engine. This style attempts to follow the GNU " +"Project's changelog guidelines<citation>web:changelog</citation>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:40 +msgid "" +"You will not be shocked to learn that Mercurial's default output style is " +"named <literal>default</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:44 +msgid "Setting a default style" +msgstr "设置默认样式" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:46 +msgid "" +"You can modify the output style that Mercurial will use for every command by " +"editing your <filename role=\"special\">~/.hgrc</filename> file, naming the " +"style you would prefer to use." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:54 +msgid "" +"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 <literal>templates</literal> " +"subdirectory of your Mercurial install directory)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:63 +msgid "Commands that support styles and templates" +msgstr "支持样式和模版的命令" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:65 +msgid "" +"All of Mercurial's <quote><literal>log</literal>-like</quote> commands let " +"you use styles and templates: <command role=\"hg-cmd\">hg incoming</command>, " +"<command role=\"hg-cmd\">hg log</command>, <command role=\"hg-cmd\">hg " +"outgoing</command>, and <command role=\"hg-cmd\">hg tip</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:72 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:80 +msgid "The basics of templating" +msgstr "模版基础" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:82 +msgid "" +"At its simplest, a Mercurial template is a piece of text. Some of the text " +"never changes, while other parts are <emphasis>expanded</emphasis>, or " +"replaced with new text, when necessary." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:87 +msgid "" +"Before we continue, let's look again at a simple example of Mercurial's " +"normal output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:92 +msgid "" +"Now, let's run the same command, but using a template to change its output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:97 +msgid "" +"The example above illustrates the simplest possible template; it's just a " +"piece of static text, printed once for each changeset. The <option role=\"hg-" +"opt-log\">--template</option> option to the <command role=\"hg-cmd\">hg log</" +"command> command tells Mercurial to use the given text as the template when " +"printing each changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:105 +msgid "" +"Notice that the template string above ends with the text <quote><literal>\\n</" +"literal></quote>. This is an <emphasis>escape sequence</emphasis>, 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 <xref " +"linkend=\"sec:template:escape\"/> for more details of escape sequences." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:113 +msgid "" +"A template that prints a fixed string of text all the time isn't very useful; " +"let's try something a bit more complex." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:119 +msgid "" +"As you can see, the string <quote><literal>{desc}</literal></quote> in the " +"template has been replaced in the output with the description of each " +"changeset. Every time Mercurial finds text enclosed in curly braces " +"(<quote><literal>{</literal></quote> and <quote><literal>}</literal></" +"quote>), 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 <xref linkend=\"sec:template:escape\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:131 +msgid "Common template keywords" +msgstr "模版关键字" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:133 +msgid "" +"You can start writing simple templates immediately using the keywords below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:137 +msgid "" +"<literal role=\"template-keyword\">author</literal>: String. The unmodified " +"author of the changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:141 +msgid "" +"<literal role=\"template-keyword\">branches</literal>: String. The name of " +"the branch on which the changeset was committed. Will be empty if the branch " +"name was <literal>default</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:147 +msgid "" +"<literal role=\"template-keyword\">date</literal>: Date information. The " +"date when the changeset was committed. This is <emphasis>not</emphasis> " +"human-readable; you must pass it through a filter that will render it " +"appropriately. See <xref linkend=\"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:158 +msgid "" +"<literal role=\"template-keyword\">desc</literal>: String. The text of the " +"changeset description." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:161 +msgid "" +"<literal role=\"template-keyword\">files</literal>: List of strings. All " +"files modified, added, or removed by this changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:166 +msgid "" +"<literal role=\"template-keyword\">file_adds</literal>: List of strings. " +"Files added by this changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:170 +msgid "" +"<literal role=\"template-keyword\">file_dels</literal>: List of strings. " +"Files removed by this changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:174 +msgid "" +"<literal role=\"template-keyword\">node</literal>: String. The changeset " +"identification hash, as a 40-character hexadecimal string." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:178 +msgid "" +"<literal role=\"template-keyword\">parents</literal>: List of strings. The " +"parents of the changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:182 +msgid "" +"<literal role=\"template-keyword\">rev</literal>: Integer. The repository-" +"local changeset revision number." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:186 +msgid "" +"<literal role=\"template-keyword\">tags</literal>: List of strings. Any tags " +"associated with the changeset." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:191 +msgid "" +"A few simple experiments will show us what to expect when we use these " +"keywords; you can see the results below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:196 +msgid "" +"As we noted above, the date keyword does not produce human-readable output, " +"so we must treat it specially. This involves using a <emphasis>filter</" +"emphasis>, about which more in <xref linkend=\"sec:template:filter\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:205 +msgid "Escape sequences" +msgstr "转义序列" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:207 +msgid "" +"Mercurial's templating engine recognises the most commonly used escape " +"sequences in strings. When it sees a backslash (<quote><literal>\\</" +"literal></quote>) character, it looks at the following character and " +"substitutes the two characters with a single replacement, as described below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:214 +msgid "" +"<literal>\\</literal>: Backslash, <quote><literal>\\</literal></quote>, ASCII " +"134." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:218 +msgid "<literal>\\n</literal>: Newline, ASCII 12." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:221 +msgid "<literal>\\r</literal>: Carriage return, ASCII 15." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:224 +msgid "<literal>\\t</literal>: Tab, ASCII 11." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:227 +msgid "<literal>\\v</literal>: Vertical tab, ASCII 13." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:230 +msgid "" +"<literal>{</literal>: Open curly brace, <quote><literal>{</literal></quote>, " +"ASCII 173." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:234 +msgid "" +"<literal>}</literal>: Close curly brace, <quote><literal>}</literal></quote>, " +"ASCII 175." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:239 +msgid "" +"As indicated above, if you want the expansion of a template to contain a " +"literal <quote><literal>\\</literal></quote>, <quote><literal>{</literal></" +"quote>, or <quote><literal>{</literal></quote> character, you must escape it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:247 +msgid "Filtering keywords to change their results" +msgstr "通过过滤关键字来修改输出结果" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:249 +msgid "" +"Some of the results of template expansion are not immediately easy to use. " +"Mercurial lets you specify an optional chain of <emphasis>filters</emphasis> " +"to modify the result of expanding a keyword. You have already seen a common " +"filter, <literal role=\"template-kw-filt-date\">isodate</literal>, in action " +"above, to make a date readable." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:256 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:263 +msgid "" +"<literal role=\"template-filter\">addbreaks</literal>: Any text. Add an XHTML " +"<quote><literal><br/></literal></quote> tag before the end of every " +"line except the last. For example, <quote><literal>foo\\nbar</literal></" +"quote> becomes <quote><literal>foo<br/>\\nbar</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:270 +msgid "" +"<literal role=\"template-kw-filt-date\">age</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render the age of the date, " +"relative to the current time. Yields a string like <quote><literal>10 " +"minutes</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:277 +msgid "" +"<literal role=\"template-filter\">basename</literal>: Any text, but most " +"useful for the <literal role=\"template-keyword\">files</literal> keyword and " +"its relatives. Treat the text as a path, and return the basename. For " +"example, <quote><literal>foo/bar/baz</literal></quote> becomes " +"<quote><literal>baz</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:286 +msgid "" +"<literal role=\"template-kw-filt-date\">date</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render a date in a similar " +"format to the Unix <literal role=\"template-keyword\">date</literal> command, " +"but with timezone included. Yields a string like <quote><literal>Mon Sep 04 " +"15:13:13 2006 -0700</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:294 +msgid "" +"<literal role=\"template-kw-filt-author\">domain</literal>: Any text, but " +"most useful for the <literal role=\"template-keyword\">author</literal> " +"keyword. Finds the first string that looks like an email address, and " +"extract just the domain component. For example, <quote><literal>Bryan " +"O'Sullivan <bos@serpentine.com></literal></quote> becomes " +"<quote><literal>serpentine.com</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:304 +msgid "" +"<literal role=\"template-kw-filt-author\">email</literal>: Any text, but most " +"useful for the <literal role=\"template-keyword\">author</literal> keyword. " +"Extract the first string that looks like an email address. For example, " +"<quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> " +"becomes <quote><literal>bos@serpentine.com</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:313 +msgid "" +"<literal role=\"template-filter\">escape</literal>: Any text. Replace the " +"special XML/XHTML characters <quote><literal>&</literal></quote>, " +"<quote><literal><</literal></quote> and <quote><literal>></literal></" +"quote> with XML entities." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:321 +msgid "" +"<literal role=\"template-filter\">fill68</literal>: Any text. Wrap the text " +"to fit in 68 columns. This is useful before you pass text through the " +"<literal role=\"template-filter\">tabindent</literal> filter, and still want " +"it to fit in an 80-column fixed-font window." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:329 +msgid "" +"<literal role=\"template-filter\">fill76</literal>: Any text. Wrap the text " +"to fit in 76 columns." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:333 +msgid "" +"<literal role=\"template-filter\">firstline</literal>: Any text. Yield the " +"first line of text, without any trailing newlines." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:338 +msgid "" +"<literal role=\"template-kw-filt-date\">hgdate</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render the date as a pair of " +"readable numbers. Yields a string like <quote><literal>1157407993 25200</" +"literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:345 +msgid "" +"<literal role=\"template-kw-filt-date\">isodate</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render the date as a text " +"string in ISO 8601 format. Yields a string like <quote><literal>2006-09-04 " +"15:13:13 -0700</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:352 +msgid "" +"<literal role=\"template-filter\">obfuscate</literal>: Any text, but most " +"useful for the <literal role=\"template-keyword\">author</literal> keyword. " +"Yield the input text rendered as a sequence of XML entities. This helps to " +"defeat some particularly stupid screen-scraping email harvesting spambots." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:360 +msgid "" +"<literal role=\"template-kw-filt-author\">person</literal>: Any text, but " +"most useful for the <literal role=\"template-keyword\">author</literal> " +"keyword. Yield the text before an email address. For example, " +"<quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> " +"becomes <quote><literal>Bryan O'Sullivan</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:369 +msgid "" +"<literal role=\"template-kw-filt-date\">rfc822date</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render a date using the same " +"format used in email headers. Yields a string like <quote><literal>Mon, 04 " +"Sep 2006 15:13:13 -0700</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:376 +msgid "" +"<literal role=\"template-kw-filt-node\">short</literal>: Changeset hash. " +"Yield the short form of a changeset hash, i.e. a 12-character hexadecimal " +"string." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:381 +msgid "" +"<literal role=\"template-kw-filt-date\">shortdate</literal>: <literal role=" +"\"template-keyword\">date</literal> keyword. Render the year, month, and day " +"of the date. Yields a string like <quote><literal>2006-09-04</literal></" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:387 +msgid "" +"<literal role=\"template-filter\">strip</literal>: Any text. Strip all " +"leading and trailing whitespace from the string." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:391 +msgid "" +"<literal role=\"template-filter\">tabindent</literal>: Any text. Yield the " +"text, with every line except the first starting with a tab character." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:396 +msgid "" +"<literal role=\"template-filter\">urlescape</literal>: Any text. Escape all " +"characters that are considered <quote>special</quote> by URL parsers. For " +"example, <literal>foo bar</literal> becomes <literal>foo%20bar</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:403 +msgid "" +"<literal role=\"template-kw-filt-author\">user</literal>: Any text, but most " +"useful for the <literal role=\"template-keyword\">author</literal> keyword. " +"Return the <quote>user</quote> portion of an email address. For example, " +"<quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> " +"becomes <quote><literal>bos</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch10-template.xml:416 +msgid "" +"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 <literal role=\"template-keyword\">desc</literal> keyword " +"into the <literal role=\"template-kw-filt-date\">isodate</literal> filter is " +"not a good idea." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:425 +msgid "Combining filters" +msgstr "组合过滤器" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:427 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:436 +msgid "" +"Note the use of <quote><literal>\\t</literal></quote> (a tab character) in " +"the template to force the first line to be indented; this is necessary since " +"<literal role=\"template-keyword\">tabindent</literal> indents all lines " +"<emphasis>except</emphasis> the first." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:442 +msgid "" +"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 <literal>fill68|tabindent</" +"literal> gives very different results from <literal>tabindent|fill68</" +"literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:453 +msgid "From templates to styles" +msgstr "从模版到样式" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:455 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:460 +msgid "" +"More than that, using a style file unlocks the power of Mercurial's " +"templating engine in ways that are not possible using the command line " +"<option role=\"hg-opt-log\">--template</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:466 +msgid "The simplest of style files" +msgstr "最简单的样式文件" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:468 +msgid "Our simple style file contains just one line:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:472 +msgid "" +"This tells Mercurial, <quote>if you're printing a changeset, use the text on " +"the right as the template</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:478 +msgid "Style file syntax" +msgstr "样式文件语法" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:480 +msgid "The syntax rules for a style file are simple." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:483 +msgid "The file is processed one line at a time." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:486 +msgid "Leading and trailing white space are ignored." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:489 +msgid "Empty lines are skipped." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:491 +msgid "" +"If a line starts with either of the characters <quote><literal>#</literal></" +"quote> or <quote><literal>;</literal></quote>, the entire line is treated as " +"a comment, and skipped as if empty." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:496 +msgid "" +"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 <literal>[A-Za-z_][A-" +"Za-z0-9_]*</literal>.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:502 +msgid "" +"The next element must be an <quote><literal>=</literal></quote> character, " +"which can be preceded or followed by an arbitrary amount of white space." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:507 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:511 +msgid "" +"If the rest of the line <emphasis>does not</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch10-template.xml:520 +msgid "Style files by example" +msgstr "样式文件例子" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:522 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:529 +msgid "Identifying mistakes in style files" +msgstr "在样式文件中定位错误" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:531 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:537 +msgid "" +"Notice that <filename>broken.style</filename> attempts to define a " +"<literal>changeset</literal> keyword, but forgets to give any content for it. " +"When instructed to use this style file, Mercurial promptly complains." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:544 +msgid "This error message looks intimidating, but it is not too hard to follow." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:548 +msgid "" +"The first component is simply Mercurial's way of saying <quote>I am giving " +"up</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:552 +msgid "Next comes the name of the style file that contains the error." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:556 +msgid "" +"Following the file name is the line number where the error was encountered." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:560 +msgid "Finally, a description of what went wrong." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:564 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:572 +msgid "Uniquely identifying a repository" +msgstr "版本库的唯一标识" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:574 +msgid "" +"If you would like to be able to identify a Mercurial repository <quote>fairly " +"uniquely</quote> using a short string as an identifier, you can use the first " +"revision in the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:581 +msgid "" +"This is not guaranteed to be unique, but it is nevertheless useful in many " +"cases." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:584 +msgid "" +"It will not work in a completely empty repository, because such a repository " +"does not have a revision zero." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:588 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:593 +msgid "Here are some uses to which you could put this identifier:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:596 +msgid "" +"As a key into a table for a database that manages repositories on a server." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:599 +msgid "" +"As half of a {<emphasis>repository ID</emphasis>, <emphasis>revision ID</" +"emphasis>} tuple. Save this information away when you run an automated build " +"or other activity, so that you can <quote>replay</quote> the build later if " +"necessary." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch10-template.xml:608 +msgid "Mimicking Subversion's output" +msgstr "模仿 Subversion 的输出" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:610 +msgid "" +"Let's try to emulate the default output format used by another revision " +"control tool, Subversion." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:615 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:622 +msgid "" +"There are a few small ways in which this template deviates from the output " +"produced by Subversion." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:625 +msgid "" +"Subversion prints a <quote>readable</quote> date (the <quote><literal>Wed, 27 " +"Sep 2006</literal></quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:632 +msgid "" +"We emulate Subversion's printing of <quote>separator</quote> lines full of " +"<quote><literal>-</literal></quote> characters by ending the template with " +"such a line. We use the templating engine's <literal role=\"template-keyword" +"\">header</literal> keyword to print a separator line as the first line of " +"output (see below), thus achieving similar output to Subversion." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:641 +msgid "" +"Subversion's output includes a count in the header of the number of lines in " +"the commit message. We cannot replicate this in Mercurial; the templating " +"engine does not currently provide a filter that counts the number of lines " +"the template generates." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:647 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch10-template.xml:654 +msgid "" +"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 " +"<quote><literal>\\n</literal></quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch11-mq.xml:5 +msgid "Managing change with Mercurial Queues" +msgstr "使用 MQ 管理修改" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:8 +msgid "The patch management problem" +msgstr "补丁的管理问题" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:10 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:20 +msgid "" +"This is a simple case of the <quote>patch management</quote> problem. You " +"have an <quote>upstream</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:27 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:32 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:37 +msgid "" +"When you have few changes to maintain, it is easy to manage a single patch " +"using the standard <command>diff</command> and <command>patch</command> " +"programs (see <xref linkend=\"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 <quote>chunks of work,</quote> so that for example a " +"single patch will contain only one bug fix (the patch might modify several " +"files, but it's doing <quote>only one thing</quote>), 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:52 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:59 +msgid "" +"Fortunately, Mercurial includes a powerful extension, Mercurial Queues (or " +"simply <quote>MQ</quote>), that massively simplifies the patch management " +"problem." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:65 +msgid "The prehistory of Mercurial Queues" +msgstr "MQ 的历史" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:67 +msgid "" +"During the late 1990s, several Linux kernel developers started to maintain " +"<quote>patch series</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:73 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:80 +msgid "A patchwork quilt" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:82 +msgid "" +"In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the approach " +"of Andrew's scripts and published a tool called <quote>patchwork quilt</" +"quote> <citation>web:quilt</citation>, or simply <quote>quilt</quote> (see " +"<citation>gruenbacher:2005</citation> for a paper describing it). Because " +"quilt substantially automated patch management, it rapidly gained a large " +"following among open source software developers." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:91 +msgid "" +"Quilt manages a <emphasis>stack of patches</emphasis> 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 <quote>refresh</quote> the patch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:99 +msgid "" +"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 <quote>tree with one patch applied</quote> to <quote>tree with two " +"patches applied</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:106 +msgid "" +"You can <emphasis>change</emphasis> which patches are applied to the tree. " +"If you <quote>pop</quote> 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 <quote>push</quote> a popped patch again, and the " +"directory tree will be restored to contain the modifications in the patch. " +"Most importantly, you can run the <quote>refresh</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:118 +msgid "" +"Quilt knows nothing about revision control tools, so it works equally well on " +"top of an unpacked tarball or a Subversion working copy." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:124 +msgid "From patchwork quilt to Mercurial Queues" +msgstr "从 patchwork quilt 到 MQ" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:126 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:130 +msgid "" +"The key difference between quilt and MQ is that quilt knows nothing about " +"revision control systems, while MQ is <emphasis>integrated</emphasis> into " +"Mercurial. Each patch that you push is represented as a Mercurial " +"changeset. Pop a patch, and the changeset goes away." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:136 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:144 +msgid "The huge advantage of MQ" +msgstr "MQ 的巨大优势" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:146 +msgid "" +"I cannot overstate the value that MQ offers through the unification of " +"patches and revision control." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:149 +msgid "" +"A major reason that patches have persisted in the free software and open " +"source world&emdash;in spite of the availability of increasingly capable " +"revision control tools over the years&emdash;is the <emphasis>agility</" +"emphasis> they offer." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:155 +msgid "" +"Traditional revision control tools make a permanent, irreversible record of " +"everything that you do. While this has great value, it's also somewhat " +"stifling. If you want to perform a wild-eyed experiment, you have to be " +"careful in how you go about it, or you risk leaving unneeded&emdash;or worse, " +"misleading or destabilising&emdash;traces of your missteps and errors in the " +"permanent revision record." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:163 +msgid "" +"By contrast, MQ's marriage of distributed revision control with patches makes " +"it much easier to isolate your work. Your patches live on top of normal " +"revision history, and you can make them disappear or reappear at will. If " +"you don't like a patch, you can drop it. If a patch isn't quite as you want " +"it to be, simply fix it&emdash;as many times as you need to, until you have " +"refined it into the form you desire." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:171 +msgid "" +"As an example, the integration of patches with revision control makes " +"understanding patches and debugging their effects&emdash;and their interplay " +"with the code they're based on&emdash;<emphasis>enormously</emphasis> easier. " +"Since every applied patch has an associated changeset, you can give <command " +"role=\"hg-cmd\">hg log</command> a file name to see which changesets and " +"patches affected the file. You can use the <command role=\"hg-cmd\">hg " +"bisect</command> command to binary-search through all changesets and applied " +"patches to see where a bug got introduced or fixed. You can use the <command " +"role=\"hg-cmd\">hg annotate</command> command to see which changeset or patch " +"modified a particular line of a source file. And so on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:189 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:193 +msgid "" +"The traditional Unix <command>diff</command> command compares two files, and " +"prints a list of differences between them. The <command>patch</command> " +"command understands these differences as <emphasis>modifications</emphasis> " +"to make to a file. Take a look below for a simple example of these commands " +"in action." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:202 +msgid "" +"The type of file that <command>diff</command> generates (and <command>patch</" +"command> takes as input) is called a <quote>patch</quote> or a <quote>diff</" +"quote>; there is no difference between a patch and a diff. (We'll use the " +"term <quote>patch</quote>, since it's more commonly used.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:208 +msgid "" +"A patch file can start with arbitrary text; the <command>patch</command> " +"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</" +"command> searches for the first line that starts with the string " +"<quote><literal>diff -</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:215 +msgid "" +"MQ works with <emphasis>unified</emphasis> diffs (<command>patch</command> " +"can accept several other diff formats, but MQ doesn't). A unified diff " +"contains two kinds of header. The <emphasis>file header</emphasis> describes " +"the file being modified; it contains the name of the file to modify. When " +"<command>patch</command> sees a new file header, it looks for a file with " +"that name to start modifying." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:223 +msgid "" +"After the file header comes a series of <emphasis>hunks</emphasis>. 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 <emphasis>context</emphasis> for the hunk. If there's " +"only a small amount of context between successive hunks, <command>diff</" +"command> doesn't print a new hunk header; it just runs the hunks together, " +"with a few lines of context between modifications." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:235 +msgid "" +"Each line of context begins with a space character. Within the hunk, a line " +"that begins with <quote><literal>-</literal></quote> means <quote>remove this " +"line,</quote> while a line that begins with <quote><literal>+</literal></" +"quote> means <quote>insert this line.</quote> For example, a line that is " +"modified is represented by one deletion and one insertion." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:243 +msgid "" +"We will return to some of the more subtle aspects of patches later (in <xref " +"linkend=\"sec:mq:adv-patch\"/>), but you should have enough information now " +"to use MQ." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:250 +msgid "Getting started with Mercurial Queues" +msgstr "开始使用 MQ" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:252 +msgid "" +"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 <filename role=" +"\"home\">~/.hgrc</filename> file, and add the lines below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:262 +msgid "" +"Once the extension is enabled, it will make a number of new commands " +"available. To verify that the extension is working, you can use <command " +"role=\"hg-cmd\">hg help</command> to see if the <command role=\"hg-ext-mq" +"\">qinit</command> command is now available." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:270 +msgid "" +"You can use MQ with <emphasis>any</emphasis> Mercurial repository, and its " +"commands only operate within that repository. To get started, simply prepare " +"the repository using the <command role=\"hg-ext-mq\">qinit</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:277 +msgid "" +"This command creates an empty directory called <filename role=\"special\" " +"class=\"directory\">.hg/patches</filename>, where MQ will keep its metadata. " +"As with many Mercurial commands, the <command role=\"hg-ext-mq\">qinit</" +"command> command prints nothing if it succeeds." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:284 +msgid "Creating a new patch" +msgstr "创建新补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:286 +msgid "" +"To begin work on a new patch, use the <command role=\"hg-ext-mq\">qnew</" +"command> command. This command takes one argument, the name of the patch to " +"create." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:290 +msgid "" +"MQ will use this as the name of an actual file in the <filename role=\"special" +"\" class=\"directory\">.hg/patches</filename> directory, as you can see below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:297 +msgid "" +"Also newly present in the <filename role=\"special\" class=\"directory\">.hg/" +"patches</filename> directory are two other files, <filename role=\"special" +"\">series</filename> and <filename role=\"special\">status</filename>. The " +"<filename role=\"special\">series</filename> file lists all of the patches " +"that MQ knows about for this repository, with one patch per line. Mercurial " +"uses the <filename role=\"special\">status</filename> file for internal book-" +"keeping; it tracks all of the patches that MQ has <emphasis>applied</" +"emphasis> in this repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch11-mq.xml:309 +msgid "" +"You may sometimes want to edit the <filename role=\"special\">series</" +"filename> file by hand; for example, to change the sequence in which some " +"patches are applied. However, manually editing the <filename role=\"special" +"\">status</filename> file is almost always a bad idea, as it's easy to " +"corrupt MQ's idea of what is happening." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:318 +msgid "" +"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 <command role=\"hg-cmd\">hg diff</command> and <command role=\"hg-cmd\">hg " +"annotate</command>, work exactly as they did before." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:326 +msgid "Refreshing a patch" +msgstr "刷新补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:328 +msgid "" +"When you reach a point where you want to save your work, use the <command " +"role=\"hg-ext-mq\">qrefresh</command> command to update the patch you are " +"working on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:334 +msgid "" +"This command folds the changes you have made in the working directory into " +"your patch, and updates its corresponding changeset to contain those changes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:338 +msgid "" +"You can run <command role=\"hg-ext-mq\">qrefresh</command> as often as you " +"like, so it's a good way to <quote>checkpoint</quote> your work. Refresh " +"your patch at an opportune time; try an experiment; and if the experiment " +"doesn't work out, <command role=\"hg-cmd\">hg revert</command> your " +"modifications back to the last time you refreshed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:349 +msgid "Stacking and tracking patches" +msgstr "堆叠和跟踪补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:351 +msgid "" +"Once you have finished working on a patch, or need to work on another, you " +"can use the <command role=\"hg-ext-mq\">qnew</command> command again to " +"create a new patch. Mercurial will apply this patch on top of your existing " +"patch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:358 +msgid "" +"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 <command role=\"hg-cmd" +"\">hg annotate</command>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:363 +msgid "" +"So far, with the exception of <command role=\"hg-ext-mq\">qnew</command> and " +"<command role=\"hg-ext-mq\">qrefresh</command>, 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 below." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:373 +msgid "" +"The <command role=\"hg-ext-mq\">qseries</command> command lists every patch " +"that MQ knows about in this repository, from oldest to newest (most recently " +"<emphasis>created</emphasis>)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:379 +msgid "" +"The <command role=\"hg-ext-mq\">qapplied</command> command lists every patch " +"that MQ has <emphasis>applied</emphasis> in this repository, again from " +"oldest to newest (most recently applied)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:388 +msgid "Manipulating the patch stack" +msgstr "操作补丁堆栈" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:390 +msgid "" +"The previous discussion implied that there must be a difference between " +"<quote>known</quote> and <quote>applied</quote> patches, and there is. MQ " +"can manage a patch without it being applied in the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:395 +msgid "" +"An <emphasis>applied</emphasis> 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 " +"<command role=\"hg-ext-mq\">qpop</command> command. MQ still <emphasis>knows " +"about</emphasis>, 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. <xref linkend=\"fig:mq:stack\"/> " +"illustrates the difference between applied and tracked patches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><figure><title> +#: ../en/ch11-mq.xml:408 +msgid "Applied and unapplied patches in the MQ patch stack" +msgstr "在 MQ 补丁堆栈中应用和撤销补丁" + +#. type: Content of: <book><chapter><sect1><sect2><figure><mediaobject> +#: ../en/ch11-mq.xml:411 +msgid "<imageobject><imagedata fileref=\"figs/mq-stack.png\"/></imageobject>" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:416 +msgid "" +"You can reapply an unapplied, or popped, patch using the <command role=\"hg-" +"ext-mq\">qpush</command> 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 below for examples of <command role=\"hg-ext-mq" +"\">qpop</command> and <command role=\"hg-ext-mq\">qpush</command> in action." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:425 +msgid "" +"Notice that once we have popped a patch or two patches, the output of " +"<command role=\"hg-ext-mq\">qseries</command> remains the same, while that of " +"<command role=\"hg-ext-mq\">qapplied</command> has changed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:433 +msgid "Pushing and popping many patches" +msgstr "压入或弹出多个补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:435 +msgid "" +"While <command role=\"hg-ext-mq\">qpush</command> and <command role=\"hg-ext-" +"mq\">qpop</command> each operate on a single patch at a time by default, you " +"can push and pop many patches in one go. The <option role=\"hg-ext-mq-cmd-" +"qpush-opt\">hg -a</option> option to <command role=\"hg-ext-mq\">qpush</" +"command> causes it to push all unapplied patches, while the <option role=\"hg-" +"ext-mq-cmd-qpop-opt\">-a</option> option to <command role=\"hg-ext-mq\">qpop</" +"command> causes it to pop all applied patches. (For some more ways to push " +"and pop many patches, see <xref linkend=\"sec:mq:perf\"/> below.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:451 +msgid "Safety checks, and overriding them" +msgstr "安全的检查,然后覆盖它们" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:453 +msgid "" +"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. " +"The example below illustrates this; the <command role=\"hg-ext-mq\">qnew</" +"command> command will not create a new patch if there are outstanding " +"changes, caused in this case by the <command role=\"hg-cmd\">hg add</command> " +"of <filename>file3</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:465 +msgid "" +"Commands that check the working directory all take an <quote>I know what I'm " +"doing</quote> option, which is always named <option>-f</option>. The exact " +"meaning of <option>-f</option> depends on the command. For example, <command " +"role=\"hg-cmd\">hg qnew <option role=\"hg-ext-mq-cmd-qnew-opt\">hg -f</" +"option></command> will incorporate any outstanding changes into the new patch " +"it creates, but <command role=\"hg-cmd\">hg qpop <option role=\"hg-ext-mq-cmd-" +"qpop-opt\">hg -f</option></command> 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> option before you use it!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:480 +msgid "Working on several patches at once" +msgstr "同时处理多个补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:482 +msgid "" +"The <command role=\"hg-ext-mq\">qrefresh</command> command always refreshes " +"the <emphasis>topmost</emphasis> 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 <emphasis>that</emphasis> patch for a while." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:489 +msgid "" +"Here's an example that illustrates how you can use this ability. Let's say " +"you're developing a new feature as two patches. The first is a change to the " +"core of your software, and the second&emdash;layered on top of the " +"first&emdash;changes the user interface to use the code you just added to the " +"core. If you notice a bug in the core while you're working on the UI patch, " +"it's easy to fix the core. Simply <command role=\"hg-ext-mq\">qrefresh</" +"command> the UI patch to save your in-progress changes, and <command role=" +"\"hg-ext-mq\">qpop</command> down to the core patch. Fix the core bug, " +"<command role=\"hg-ext-mq\">qrefresh</command> the core patch, and <command " +"role=\"hg-ext-mq\">qpush</command> back to the UI patch to continue where you " +"left off." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:506 +msgid "More about patches" +msgstr "关于补丁的更多信息" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:508 +msgid "" +"MQ uses the GNU <command>patch</command> command to apply patches, so it's " +"helpful to know a few more detailed aspects of how <command>patch</command> " +"works, and about patches themselves." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:514 +msgid "The strip count" +msgstr "修剪计数" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:516 +msgid "" +"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)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:523 +msgid "" +"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 <option role=\"cmd-opt-diff" +"\">-r</option> and <option role=\"cmd-opt-diff\">-N</option> options to " +"<command>diff</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:536 +msgid "" +"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> command has a <option role=\"cmd-opt-patch\">-p</" +"option> option that indicates the number of leading path name components to " +"strip when trying to apply a patch. This number is called the " +"<emphasis>strip count</emphasis>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:544 +msgid "" +"An option of <quote><literal>-p1</literal></quote> means <quote>use a strip " +"count of one</quote>. If <command>patch</command> sees a file name " +"<filename>foo/bar/baz</filename> in a file header, it will strip " +"<filename>foo</filename> and try to patch a file named <filename>bar/baz</" +"filename>. (Strictly speaking, the strip count refers to the number of " +"<emphasis>path separators</emphasis> (and the components that go with them ) " +"to strip. A strip count of one will turn <filename>foo/bar</filename> into " +"<filename>bar</filename>, but <filename>/foo/bar</filename> (notice the extra " +"leading slash) into <filename>foo/bar</filename>.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:557 +msgid "" +"The <quote>standard</quote> strip count for patches is one; almost all " +"patches contain one leading path name component that needs to be stripped. " +"Mercurial's <command role=\"hg-cmd\">hg diff</command> command generates path " +"names in this form, and the <command role=\"hg-cmd\">hg import</command> " +"command and MQ expect patches to have a strip count of one." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:565 +msgid "" +"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 <command " +"role=\"hg-ext-mq\">qimport</command> the patch, because <command role=\"hg-" +"ext-mq\">qimport</command> does not yet have a <literal>-p</literal> option " +"(see <ulink role=\"hg-bug\" url=\"http://www.selenic.com/mercurial/bts/" +"issue311\">issue 311</ulink>). Your best bet is to <command role=\"hg-ext-mq" +"\">qnew</command> a patch of your own, then use <command>patch -pN</command> " +"to apply their patch, followed by <command role=\"hg-cmd\">hg addremove</" +"command> to pick up any files added or removed by the patch, followed by " +"<command role=\"hg-ext-mq\">hg qrefresh</command>. This complexity may become " +"unnecessary; see <ulink role=\"hg-bug\" url=\"http://www.selenic.com/" +"mercurial/bts/issue311\">issue 311</ulink> for details." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:584 +msgid "Strategies for applying a patch" +msgstr "应用补丁的策略" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:586 +msgid "" +"When <command>patch</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:593 +msgid "" +"First, <command>patch</command> 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 <emphasis>offset</" +"emphasis> from the original line number." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:602 +msgid "" +"If a context-only match fails, <command>patch</command> removes the first and " +"last lines of the context, and tries a <emphasis>reduced</emphasis> context-" +"only match. If the hunk with reduced context succeeds, it prints a message " +"saying that it applied the hunk with a <emphasis>fuzz factor</emphasis> (the " +"number after the fuzz factor indicates how many lines of context " +"<command>patch</command> had to trim before the patch applied)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:611 +msgid "" +"When neither of these techniques works, <command>patch</command> prints a " +"message saying that the hunk in question was rejected. It saves rejected " +"hunks (also simply called <quote>rejects</quote>) to a file with the same " +"name, and an added <filename role=\"special\">.rej</filename> extension. It " +"also saves an unmodified copy of the file with a <filename role=\"special\">." +"orig</filename> extension; the copy of the file without any extensions will " +"contain any changes made by hunks that <emphasis>did</emphasis> apply " +"cleanly. If you have a patch that modifies <filename>foo</filename> with six " +"hunks, and one of them fails to apply, you will have: an unmodified " +"<filename>foo.orig</filename>, a <filename>foo.rej</filename> containing one " +"hunk, and <filename>foo</filename>, containing the changes made by the five " +"successful hunks." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:629 +msgid "Some quirks of patch representation" +msgstr "补丁的一些特性" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:631 +msgid "" +"There are a few useful things to know about how <command>patch</command> " +"works with files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:634 +msgid "" +"This should already be obvious, but <command>patch</command> cannot handle " +"binary files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:638 +msgid "" +"Neither does it care about the executable bit; it creates new files as " +"readable, but not executable." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:642 +msgid "" +"<command>patch</command> treats the removal of a file as a diff between the " +"file to be removed and the empty file. So your idea of <quote>I deleted this " +"file</quote> looks like <quote>every line of this file was deleted</quote> in " +"a patch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:648 +msgid "" +"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 <quote>I added this file</" +"quote> looks like <quote>every line of this file was added</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:654 +msgid "" +"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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:660 +msgid "" +"<command>patch</command> cannot represent empty files, so you cannot use a " +"patch to represent the notion <quote>I added this empty file to the tree</" +"quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:667 +msgid "Beware the fuzz" +msgstr "当心毛刺" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:669 +msgid "" +"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</command> or <command role=\"hg-ext-mq\">qpush</command> ever " +"mentions an offset or fuzz factor, you should make sure that the modified " +"files are correct afterwards." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:679 +msgid "" +"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 <quote>often,</quote> not <quote>always,</" +"quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:692 +msgid "Handling rejection" +msgstr "处理拒绝" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:694 +msgid "" +"If <command role=\"hg-ext-mq\">qpush</command> fails to apply a patch, it " +"will print an error message and exit. If it has left <filename role=\"special" +"\">.rej</filename> files behind, it is usually best to fix up the rejected " +"hunks before you push more patches or do any further work." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:700 +msgid "" +"If your patch <emphasis>used to</emphasis> apply cleanly, and no longer does " +"because you've changed the underlying code that your patches are based on, " +"Mercurial Queues can help; see <xref linkend=\"sec:mq:merge\"/> for details." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:705 +msgid "" +"Unfortunately, there aren't any great techniques for dealing with rejected " +"hunks. Most often, you'll need to view the <filename role=\"special\">.rej</" +"filename> file and edit the target file, applying the rejected hunks by hand." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:710 +msgid "" +"If you're feeling adventurous, Neil Brown, a Linux kernel hacker, wrote a " +"tool called <command>wiggle</command> <citation>web:wiggle</citation>, which " +"is more vigorous than <command>patch</command> in its attempts to make a " +"patch apply." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:716 +msgid "" +"Another Linux kernel hacker, Chris Mason (the author of Mercurial Queues), " +"wrote a similar tool called <command>mpatch</command> <citation>web:mpatch</" +"citation>, which takes a simple approach to automating the application of " +"hunks rejected by <command>patch</command>. The <command>mpatch</command> " +"command can help with four common reasons that a hunk may be rejected:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:725 +msgid "The context in the middle of a hunk has changed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:728 +msgid "A hunk is missing some context at the beginning or end." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:731 +msgid "" +"A large hunk might apply better&emdash;either entirely or in part&emdash;if " +"it was broken up into smaller hunks." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:735 +msgid "" +"A hunk removes lines with slightly different content than those currently " +"present in the file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:739 +msgid "" +"If you use <command>wiggle</command> or <command>mpatch</command>, you should " +"be doubly careful to check your results when you're done. In fact, " +"<command>mpatch</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:751 +msgid "Getting the best performance out of MQ" +msgstr "MQ 的性能" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:753 +msgid "" +"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 <citation>web:europython</citation>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:762 +msgid "" +"On my old, slow laptop, I was able to <command role=\"hg-cmd\">hg qpush " +"<option role=\"hg-ext-mq-cmd-qpush-opt\">hg -a</option></command> all 1,738 " +"patches in 3.5 minutes, and <command role=\"hg-cmd\">hg qpop <option role=" +"\"hg-ext-mq-cmd-qpop-opt\">hg -a</option></command> them all in 30 seconds. " +"(On a newer laptop, the time to push all patches dropped to two minutes.) I " +"could <command role=\"hg-ext-mq\">qrefresh</command> one of the biggest " +"patches (which made 22,779 lines of changes to 287 files) in 6.6 seconds." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:773 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:777 +msgid "" +"First of all, try to <quote>batch</quote> operations together. Every time " +"you run <command role=\"hg-ext-mq\">qpush</command> or <command role=\"hg-ext-" +"mq\">qpop</command>, these commands scan the working directory once to make " +"sure you haven't made some changes and then forgotten to run <command role=" +"\"hg-ext-mq\">qrefresh</command>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:788 +msgid "" +"The <command role=\"hg-ext-mq\">qpush</command> and <command role=\"hg-ext-mq" +"\">qpop</command> commands allow you to push and pop multiple patches at a " +"time. You can identify the <quote>destination patch</quote> that you want to " +"end up at. When you <command role=\"hg-ext-mq\">qpush</command> with a " +"destination specified, it will push patches until that patch is at the top of " +"the applied stack. When you <command role=\"hg-ext-mq\">qpop</command> to a " +"destination, MQ will pop patches until the destination patch is at the top." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:798 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:805 +msgid "Updating your patches when the underlying code changes" +msgstr "当基础代码改变时,更新补丁的方法" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:808 +msgid "" +"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 <emphasis>rebasing</emphasis> your patch series." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:817 +msgid "" +"The simplest way to do this is to <command role=\"hg-cmd\">hg qpop <option " +"role=\"hg-ext-mq-cmd-qpop-opt\">hg -a</option></command> your patches, then " +"<command role=\"hg-cmd\">hg pull</command> changes into the underlying " +"repository, and finally <command role=\"hg-cmd\">hg qpush <option role=\"hg-" +"ext-mq-cmd-qpop-opt\">hg -a</option></command> 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, <command role=\"hg-ext-mq" +"\">qrefresh</command> the affected patch, and continue pushing until you have " +"fixed your entire stack." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:829 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:836 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:842 +msgid "The process is a little involved." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch11-mq.xml:844 +msgid "" +"To begin, <command role=\"hg-cmd\">hg qpush -a</command> all of your patches " +"on top of the revision where you know that they apply cleanly." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch11-mq.xml:848 +msgid "" +"Save a backup copy of your patch directory using <command role=\"hg-cmd\">hg " +"qsave <option role=\"hg-ext-mq-cmd-qsave-opt\">hg -e</option> <option role=" +"\"hg-ext-mq-cmd-qsave-opt\">hg -c</option></command>. This prints the name " +"of the directory that it has saved the patches in. It will save the patches " +"to a directory called <filename role=\"special\" class=\"directory\">.hg/" +"patches.N</filename>, where <literal>N</literal> is a small integer. It also " +"commits a <quote>save changeset</quote> on top of your applied patches; this " +"is for internal book-keeping, and records the states of the <filename role=" +"\"special\">series</filename> and <filename role=\"special\">status</" +"filename> files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch11-mq.xml:862 +msgid "" +"Use <command role=\"hg-cmd\">hg pull</command> to bring new changes into the " +"underlying repository. (Don't run <command role=\"hg-cmd\">hg pull -u</" +"command>; see below for why.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch11-mq.xml:867 +msgid "" +"Update to the new tip revision, using <command role=\"hg-cmd\">hg update " +"<option role=\"hg-opt-update\">-C</option></command> to override the patches " +"you have pushed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch11-mq.xml:872 +msgid "" +"Merge all patches using <command>hg qpush -m -a</command>. The <option role=" +"\"hg-ext-mq-cmd-qpush-opt\">-m</option> option to <command role=\"hg-ext-mq" +"\">qpush</command> tells MQ to perform a three-way merge if the patch fails " +"to apply." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:880 +msgid "" +"During the <command role=\"hg-cmd\">hg qpush <option role=\"hg-ext-mq-cmd-" +"qpush-opt\">hg -m</option></command>, each patch in the <filename role=" +"\"special\">series</filename> file is applied normally. If a patch applies " +"with fuzz or rejects, MQ looks at the queue you <command role=\"hg-ext-mq" +"\">qsave</command>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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:890 +msgid "" +"When you finish resolving the effects of a patch, MQ refreshes your patch " +"based on the result of the merge." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:893 +msgid "" +"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 <filename role=" +"\"special\" class=\"directory\">.hg/patches.N</filename>. You can remove the " +"extra head using <command role=\"hg-cmd\">hg qpop -a -n patches.N</command> " +"or <command role=\"hg-cmd\">hg strip</command>. You can delete <filename " +"role=\"special\" class=\"directory\">.hg/patches.N</filename> once you are " +"sure that you no longer need it as a backup." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:905 +msgid "Identifying patches" +msgstr "标识补丁" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:907 +msgid "" +"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</filename> to <command role=\"hg-ext-mq\">qpush</" +"command>, for example, and it will push patches until <filename>foo.patch</" +"filename> is applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:914 +msgid "" +"As a shortcut, you can refer to a patch using both a name and a numeric " +"offset; <literal>foo.patch-2</literal> means <quote>two patches before " +"<literal>foo.patch</literal></quote>, while <literal>bar.patch+4</literal> " +"means <quote>four patches after <literal>bar.patch</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:920 +msgid "" +"Referring to a patch by index isn't much different. The first patch printed " +"in the output of <command role=\"hg-ext-mq\">qseries</command> is patch zero " +"(yes, it's one of those start-at-zero counting systems); the second is patch " +"one; and so on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:926 +msgid "" +"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 <literal role=\"tag\">qbase</literal> and <literal role=\"tag" +"\">qtip</literal> identify the <quote>bottom-most</quote> and topmost applied " +"patches, respectively." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:936 +msgid "" +"These additions to Mercurial's normal tagging capabilities make dealing with " +"patches even more of a breeze." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:939 +msgid "Want to patchbomb a mailing list with your latest series of changes?" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:942 +msgid "" +"(Don't know what <quote>patchbombing</quote> is? See <xref linkend=\"sec:" +"hgext:patchbomb\"/>.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:945 +msgid "" +"Need to see all of the patches since <literal>foo.patch</literal> that have " +"touched files in a subdirectory of your tree?" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:952 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:957 +msgid "" +"Another nice consequence of representing patch names as tags is that when you " +"run the <command role=\"hg-cmd\">hg log</command> 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 <quote>normal</" +"quote> revisions. The following example shows a few normal Mercurial " +"commands in use with applied patches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:970 +msgid "Useful things to know about" +msgstr "其它需要了解的东西" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:972 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:977 +msgid "" +"Normally, when you <command role=\"hg-ext-mq\">qpop</command> a patch and " +"<command role=\"hg-ext-mq\">qpush</command> it again, the changeset that " +"represents the patch after the pop/push will have a <emphasis>different " +"identity</emphasis> than the changeset that represented the hash beforehand. " +"See <xref linkend=\"sec:mqref:cmd:qpush\"/> for information as to why this is." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:986 +msgid "" +"It's not a good idea to <command role=\"hg-cmd\">hg merge</command> changes " +"from another branch with a patch changeset, at least if you want to maintain " +"the <quote>patchiness</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:997 +msgid "Managing patches in a repository" +msgstr "在版本库管理补丁" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:999 +msgid "" +"Because MQ's <filename role=\"special\" class=\"directory\">.hg/patches</" +"filename> directory resides outside a Mercurial repository's working " +"directory, the <quote>underlying</quote> Mercurial repository knows nothing " +"about the management or presence of patches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1005 +msgid "" +"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, " +"<command role=\"hg-ext-mq\">qrefresh</command> it, then <command role=\"hg-cmd" +"\">hg commit</command> the current state of the patch. This lets you " +"<quote>roll back</quote> to that version of the patch later on." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1014 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1024 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:1030 +msgid "MQ support for patch repositories" +msgstr "MQ 支持补丁版本库" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1032 +msgid "" +"MQ helps you to work with the <filename role=\"special\" class=\"directory\">." +"hg/patches</filename> directory as a repository; when you prepare a " +"repository for working with patches using <command role=\"hg-ext-mq\">qinit</" +"command>, you can pass the <option role=\"hg-ext-mq-cmd-qinit-opt\">hg -c</" +"option> option to create the <filename role=\"special\" class=\"directory\">." +"hg/patches</filename> directory as a Mercurial repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch11-mq.xml:1042 +msgid "" +"If you forget to use the <option role=\"hg-ext-mq-cmd-qinit-opt\">hg -c</" +"option> option, you can simply go into the <filename role=\"special\" class=" +"\"directory\">.hg/patches</filename> directory at any time and run <command " +"role=\"hg-cmd\">hg init</command>. Don't forget to add an entry for the " +"<filename role=\"special\">status</filename> file to the <filename role=" +"\"special\">.hgignore</filename> file, though" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch11-mq.xml:1051 +msgid "" +"(<command role=\"hg-cmd\">hg qinit <option role=\"hg-ext-mq-cmd-qinit-opt" +"\">hg -c</option></command> does this for you automatically); you " +"<emphasis>really</emphasis> don't want to manage the <filename role=\"special" +"\">status</filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1058 +msgid "" +"As a convenience, if MQ notices that the <filename class=\"directory\">.hg/" +"patches</filename> directory is a repository, it will automatically <command " +"role=\"hg-cmd\">hg add</command> every patch that you create and import." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1063 +msgid "" +"MQ provides a shortcut command, <command role=\"hg-ext-mq\">qcommit</" +"command>, that runs <command role=\"hg-cmd\">hg commit</command> in the " +"<filename role=\"special\" class=\"directory\">.hg/patches</filename> " +"directory. This saves some bothersome typing." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1069 +msgid "" +"Finally, as a convenience to manage the patch directory, you can define the " +"alias <command>mq</command> on Unix systems. For example, on Linux systems " +"using the <command>bash</command> shell, you can include the following " +"snippet in your <filename role=\"home\">~/.bashrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1078 +msgid "" +"You can then issue commands of the form <command>mq pull</command> from the " +"main repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:1083 +msgid "A few things to watch out for" +msgstr "需要注意的事情" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1085 +msgid "" +"MQ's support for working with a repository full of patches is limited in a " +"few small respects." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1088 +msgid "" +"MQ cannot automatically detect changes that you make to the patch directory. " +"If you <command role=\"hg-cmd\">hg pull</command>, manually edit, or <command " +"role=\"hg-cmd\">hg update</command> changes to patches or the <filename role=" +"\"special\">series</filename> file, you will have to <command role=\"hg-cmd" +"\">hg qpop <option role=\"hg-ext-mq-cmd-qpop-opt\">hg -a</option></command> " +"and then <command role=\"hg-cmd\">hg qpush <option role=\"hg-ext-mq-cmd-qpush-" +"opt\">hg -a</option></command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:1104 +msgid "Third party tools for working with patches" +msgstr "操作补丁的第三方工具" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1106 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1110 +msgid "" +"The <command>diffstat</command> command <citation>web:diffstat</citation> " +"generates a histogram of the modifications made to each file in a patch. It " +"provides a good way to <quote>get a sense of</quote> a patch&emdash;which " +"files it affects, and how much change it introduces to each file and as a " +"whole. (I find that it's a good idea to use <command>diffstat</command>'s " +"<option role=\"cmd-opt-diffstat\">-p</option> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1124 +msgid "" +"The <literal role=\"package\">patchutils</literal> package <citation>web:" +"patchutils</citation> is invaluable. It provides a set of small utilities " +"that follow the <quote>Unix philosophy;</quote> each does one useful thing " +"with a patch. The <literal role=\"package\">patchutils</literal> command I " +"use most is <command>filterdiff</command>, 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</" +"command> can generate a smaller patch that only touches files whose names " +"match a particular glob pattern. See <xref linkend=\"mq-collab:tips:interdiff" +"\"/> for another example." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:1140 +msgid "Good ways to work with patches" +msgstr "操作补丁的好习惯" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1142 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1148 +msgid "" +"Give your patches descriptive names. A good name for a patch might be " +"<filename>rework-device-alloc.patch</filename>, 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 <emphasis>will</" +"emphasis> be running commands like <command role=\"hg-ext-mq\">qapplied</" +"command> and <command role=\"hg-ext-mq\">qtop</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1160 +msgid "" +"Be aware of what patch you're working on. Use the <command role=\"hg-ext-mq" +"\">qtop</command> command and skim over the text of your patches " +"frequently&emdash;for example, using <command role=\"hg-cmd\">hg tip <option " +"role=\"hg-opt-tip\">-p</option></command>)&emdash;to be sure of where you " +"stand. I have several times worked on and <command role=\"hg-ext-mq" +"\">qrefresh</command>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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1170 +msgid "" +"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 <xref linkend=\"sec:mq:" +"tools\"/>, particularly <command>diffstat</command> and <command>filterdiff</" +"command>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:1181 +msgid "MQ cookbook" +msgstr "MQ 手册" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:1184 +msgid "Manage <quote>trivial</quote> patches" +msgstr "管理<quote>琐碎的</quote>补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1186 +msgid "" +"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." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1191 +msgid "" +"Begin by downloading and unpacking the source tarball, and turning it into a " +"Mercurial repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1196 +msgid "Continue by creating a patch stack and making your changes." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1201 +msgid "" +"Let's say a few weeks or months pass, and your package author releases a new " +"version. First, bring their changes into the repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1207 +msgid "" +"The pipeline starting with <command role=\"hg-cmd\">hg locate</command> above " +"deletes all files in the working directory, so that <command role=\"hg-cmd" +"\">hg commit</command>'s <option role=\"hg-opt-commit\">--addremove</option> " +"option can actually tell which files have really been removed in the newer " +"version of the source." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1215 +msgid "Finally, you can apply your patches on top of the new tree." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:1222 +msgid "Combining entire patches" +msgstr "组合全部的补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1224 +msgid "" +"MQ provides a command, <command role=\"hg-ext-mq\">qfold</command> that lets " +"you combine entire patches. This <quote>folds</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1232 +msgid "" +"The order in which you fold patches matters. If your topmost applied patch " +"is <literal>foo</literal>, and you <command role=\"hg-ext-mq\">qfold</" +"command> <literal>bar</literal> and <literal>quux</literal> into it, you will " +"end up with a patch that has the same effect as if you applied first " +"<literal>foo</literal>, then <literal>bar</literal>, followed by " +"<literal>quux</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch11-mq.xml:1243 +msgid "Merging part of one patch into another" +msgstr "合并补丁的部分内容到其它补丁" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1245 +msgid "" +"Merging <emphasis>part</emphasis> of one patch into another is more difficult " +"than combining entire patches." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1249 +msgid "" +"If you want to move changes to entire files, you can use <command>filterdiff</" +"command>'s <option role=\"cmd-opt-filterdiff\">-i</option> and <option role=" +"\"cmd-opt-filterdiff\">-x</option> 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 " +"<command role=\"hg-ext-mq\">qpush</command> it (from the hunks you moved into " +"the other patch), and you can simply <command role=\"hg-ext-mq\">qrefresh</" +"command> the patch to drop the duplicate hunks." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1262 +msgid "" +"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 <command>lsdiff -nvv</command> to print some " +"metadata about the patch." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1270 +msgid "This command prints three different kinds of number:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:1273 +msgid "" +"(in the first column) a <emphasis>file number</emphasis> to identify each " +"file modified in the patch;" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:1277 +msgid "" +"(on the next line, indented) the line number within a modified file where a " +"hunk starts; and" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch11-mq.xml:1280 +msgid "" +"(on the same line) a <emphasis>hunk number</emphasis> to identify that hunk." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1284 +msgid "" +"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</command>'s <option role=\"cmd-opt-filterdiff\">--" +"files</option> and <option role=\"cmd-opt-filterdiff\">--hunks</option> " +"options, to select exactly the file and hunk you want to extract." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch11-mq.xml:1292 +msgid "" +"Once you have this hunk, you can concatenate it onto the end of your " +"destination patch and continue with the remainder of <xref linkend=\"sec:mq:" +"combine\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch11-mq.xml:1299 +msgid "Differences between quilt and MQ" +msgstr "MQ 与 quilt 的区别" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1301 +msgid "" +"If you are already familiar with quilt, MQ provides a similar command set. " +"There are a few differences in the way that it works." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:1305 +msgid "" +"You will already have noticed that most quilt commands have MQ counterparts " +"that simply begin with a <quote><literal>q</literal></quote>. The exceptions " +"are quilt's <literal>add</literal> and <literal>remove</literal> commands, " +"the counterparts for which are the normal Mercurial <command role=\"hg-cmd" +"\">hg add</command> and <command role=\"hg-cmd\">hg remove</command> " +"commands. There is no MQ equivalent of the quilt <literal>edit</literal> " +"command." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch12-mq-collab.xml:5 +msgid "Advanced uses of Mercurial Queues" +msgstr "MQ 的高级用法" + +#. type: Content of: <book><chapter><para> +#: ../en/ch12-mq-collab.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch12-mq-collab.xml:12 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch12-mq-collab.xml:18 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:24 +msgid "The problem of many targets" +msgstr "多个目标的问题" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:26 +msgid "" +"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 <emphasis>compile</emphasis> correctly against, " +"typically, any other version." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:33 +msgid "" +"To maintain a driver, we have to keep a number of distinct versions of Linux " +"in mind." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:36 +msgid "" +"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 <quote>drive-by</quote> modifications to the driver as they develop " +"and refine kernel subsystems." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:42 +msgid "" +"We also maintain a number of <quote>backports</quote> 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 " +"<emphasis>backport</emphasis> 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.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:50 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch12-mq-collab.xml:58 +msgid "Tempting approaches that don't work well" +msgstr "工作不好的诱人方法" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:60 +msgid "" +"There are two <quote>standard</quote> ways to maintain a piece of software " +"that has to target many different environments." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:64 +msgid "" +"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 <quote>pristine</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:74 +msgid "" +"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 <quote>ifdefs</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:83 +msgid "" +"Neither of these approaches is well suited to a situation where you don't " +"<quote>own</quote> 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 <quote>my</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:93 +msgid "" +"These approaches have the added weakness of making it difficult to generate " +"well-formed patches to submit upstream." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:97 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:105 +msgid "Conditionally applying patches with guards" +msgstr "有条件的应用补丁" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:107 +msgid "" +"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 <quote>guards</quote> (which originates with quilt's " +"<literal>guards</literal> command) that does just this. To start off, let's " +"create a simple repository for experimenting in." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:116 +msgid "" +"This gives us a tiny repository that contains two patches that don't have any " +"dependencies on each other, because they touch different files." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:120 +msgid "" +"The idea behind conditional application is that you can <quote>tag</quote> a " +"patch with a <emphasis>guard</emphasis>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:127 +msgid "" +"A patch can have an arbitrary number of guards; each one is " +"<emphasis>positive</emphasis> (<quote>apply this patch if this guard is " +"selected</quote>) or <emphasis>negative</emphasis> (<quote>skip this patch if " +"this guard is selected</quote>). A patch with no guards is always applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:135 +msgid "Controlling the guards on a patch" +msgstr "控制补丁的应用条件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:137 +msgid "" +"The <command role=\"hg-ext-mq\">qguard</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:144 +msgid "" +"To set a positive guard on a patch, prefix the name of the guard with a " +"<quote><literal>+</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:149 +msgid "" +"To set a negative guard on a patch, prefix the name of the guard with a " +"<quote><literal>-</literal></quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch12-mq-collab.xml:156 +msgid "" +"The <command role=\"hg-ext-mq\">qguard</command> command <emphasis>sets</" +"emphasis> the guards on a patch; it doesn't <emphasis>modify</emphasis> " +"them. What this means is that if you run <command role=\"hg-cmd\">hg qguard " +"+a +b</command> on a patch, then <command role=\"hg-cmd\">hg qguard +c</" +"command> on the same patch, the <emphasis>only</emphasis> guard that will be " +"set on it afterwards is <literal>+c</literal>." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:165 +msgid "" +"Mercurial stores guards in the <filename role=\"special\">series</filename> " +"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 <command role=\"hg-" +"ext-mq\">qguard</command> command if you don't want to; it's okay to simply " +"edit the <filename role=\"special\">series</filename> file.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:177 +msgid "Selecting the guards to use" +msgstr "选择使用的条件" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:179 +msgid "" +"The <command role=\"hg-ext-mq\">qselect</command> 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 <command role=\"hg-ext-mq" +"\">qpush</command>. It has no other effect; in particular, it doesn't do " +"anything to patches that are already applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:186 +msgid "" +"With no arguments, the <command role=\"hg-ext-mq\">qselect</command> command " +"lists the guards currently in effect, one per line of output. Each argument " +"is treated as the name of a guard to apply." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:193 +msgid "" +"In case you're interested, the currently selected guards are stored in the " +"<filename role=\"special\">guards</filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:198 +msgid "" +"We can see the effect the selected guards have when we run <command role=\"hg-" +"ext-mq\">qpush</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:203 +msgid "" +"A guard cannot start with a <quote><literal>+</literal></quote> or " +"<quote><literal>-</literal></quote> character. The name of a guard must not " +"contain white space, but most other characters are acceptable. If you try to " +"use a guard with an invalid name, MQ will complain:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:212 +msgid "Changing the selected guards changes the patches that are applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:217 +msgid "" +"You can see in the example below that negative guards take precedence over " +"positive guards." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:224 +msgid "MQ's rules for applying patches" +msgstr "MQ 应用补丁的规则" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:226 +msgid "" +"The rules that MQ uses when deciding whether to apply a patch are as follows." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:229 +msgid "A patch that has no guards is always applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:232 +msgid "" +"If the patch has any negative guard that matches any currently selected " +"guard, the patch is skipped." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:235 +msgid "" +"If the patch has any positive guard that matches any currently selected " +"guard, the patch is applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:238 +msgid "" +"If the patch has positive or negative guards, but none matches any currently " +"selected guard, the patch is skipped." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:245 +msgid "Trimming the work environment" +msgstr "修剪工作环境" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:247 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:254 +msgid "" +"I then choose a <quote>base</quote> 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 " +"<quote>shape</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:263 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:270 +msgid "Dividing up the <filename role=\"special\">series</filename> file" +msgstr "分类补丁<filename role=\"special\">系列</filename>" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:273 +msgid "" +"I categorise the patches in the <filename role=\"special\">series</filename> " +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:279 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:283 +msgid "" +"The <quote>accepted</quote> 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 <quote>read only</quote> patches, present only to " +"transform the tree into a similar state as it is in the upstream maintainer's " +"repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:291 +msgid "" +"The <quote>rework</quote> group. Patches that I have submitted, but that the " +"upstream maintainer has requested modifications to before he will accept them." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:296 +msgid "" +"The <quote>pending</quote> group. Patches that I have not yet submitted to " +"the upstream maintainer, but which we have finished working on. These will be " +"<quote>read only</quote> for a while. If the upstream maintainer accepts " +"them upon submission, I'll move them to the end of the <quote>accepted</" +"quote> group. If he requests that I modify any, I'll move them to the " +"beginning of the <quote>rework</quote> group." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:305 +msgid "" +"The <quote>in progress</quote> group. Patches that are actively being " +"developed, and should not be submitted anywhere yet." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:309 +msgid "" +"The <quote>backport</quote> group. Patches that adapt the source tree to " +"older versions of the kernel tree." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:313 +msgid "" +"The <quote>do not ship</quote> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:321 +msgid "" +"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 <filename role=" +"\"special\">series</filename> file serves this purpose." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:329 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:334 +msgid "" +"The <quote>backport</quote> and <quote>do not ship</quote> patches float at " +"the end of the <filename role=\"special\">series</filename> file. The " +"backport patches must be applied on top of all other patches, and the " +"<quote>do not ship</quote> patches might as well stay out of harm's way." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:343 +msgid "Maintaining the patch series" +msgstr "维护补丁系列" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:345 +msgid "" +"In my work, I use a number of guards to control which patches are to be " +"applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:349 +msgid "" +"<quote>Accepted</quote> patches are guarded with <literal>accepted</" +"literal>. 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:356 +msgid "" +"Patches that are <quote>finished</quote>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:362 +msgid "" +"Those patches that need reworking before being resubmitted are guarded with " +"<literal>rework</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:366 +msgid "" +"For those patches that are still under development, I use <literal>devel</" +"literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch12-mq-collab.xml:369 +msgid "" +"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 <literal>2.6.9</literal> guard." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch12-mq-collab.xml:374 +msgid "" +"This variety of guards gives me considerable flexibility in determining what " +"kind of source tree I want to end up with. For most situations, the " +"selection of appropriate guards is automated during the build process, but I " +"can manually tune the guards to use for less common circumstances." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch12-mq-collab.xml:381 +msgid "The art of writing backport patches" +msgstr "编写向后移植补丁的艺术" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:383 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:389 +msgid "" +"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 <quote>rat's " +"nest</quote> effect of lots of <literal>#ifdef</literal>s (hunks of source " +"code that are only used conditionally) in your code, don't introduce version-" +"dependent <literal>#ifdef</literal>s into the patches. Instead, write " +"several patches, each of which makes unconditional changes, and control their " +"application using guards." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:402 +msgid "" +"There are two reasons to divide backport patches into a distinct group, away " +"from the <quote>regular</quote> patches whose effects they modify. The first " +"is that intermingling the two makes it more difficult to use a tool like the " +"<literal role=\"hg-ext\">patchbomb</literal> 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 " +"<emphasis>without</emphasis> the earlier backport patch already being applied." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch12-mq-collab.xml:417 +msgid "Useful tips for developing with MQ" +msgstr "使用 MQ 开发的技巧" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch12-mq-collab.xml:420 +msgid "Organising patches in directories" +msgstr "将补丁放到几个目录中" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:422 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:427 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch12-mq-collab.xml:434 +msgid "Viewing the history of a patch" +msgstr "察看补丁的历史" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:436 +msgid "" +"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 <xref linkend=\"sec:mq:repo\"/" +">. If you do so, you'll quickly discover that using the <command role=\"hg-" +"cmd\">hg diff</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:448 +msgid "" +"However, you can use the <literal role=\"hg-ext\">extdiff</literal> " +"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 <literal role=\"package\">patchutils</literal> <citation>web:" +"patchutils</citation>. This provides a command named <command>interdiff</" +"command>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:459 +msgid "" +"You can enable the <literal role=\"hg-ext\">extdiff</literal> extension in " +"the usual way, by adding a line to the <literal role=\"rc-extensions" +"\">extensions</literal> section of your <filename role=\"special\">~/.hgrc</" +"filename>." +msgstr "" + +# +#. &example.hg-interdiff; +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:466 +msgid "" +"The <command>interdiff</command> command expects to be passed the names of " +"two files, but the <literal role=\"hg-ext\">extdiff</literal> 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</command> on each pair of files in these two directories. " +"This program is available as <filename role=\"special\">hg-interdiff</" +"filename> in the <filename class=\"directory\">examples</filename> directory " +"of the source code repository that accompanies this book." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:478 +msgid "" +"With the <filename role=\"special\">hg-interdiff</filename> program in your " +"shell's search path, you can run it as follows, from inside an MQ patch " +"directory:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:482 +msgid "" +"Since you'll probably want to use this long-winded command a lot, you can get " +"<literal role=\"hg-ext\">hgext</literal> to make it available as a normal " +"Mercurial command, again by editing your <filename role=\"special\">~/.hgrc</" +"filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:489 +msgid "" +"This directs <literal role=\"hg-ext\">hgext</literal> to make an " +"<literal>interdiff</literal> command available, so you can now shorten the " +"previous invocation of <command role=\"hg-ext-extdiff\">extdiff</command> to " +"something a little more wieldy." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><note><para> +#: ../en/ch12-mq-collab.xml:497 +msgid "" +"The <command>interdiff</command> 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</command> may not produce useful output." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch12-mq-collab.xml:505 +msgid "" +"The <literal role=\"hg-ext\">extdiff</literal> extension is useful for more " +"than merely improving the presentation of MQ patches. To read more about it, " +"go to <xref linkend=\"sec:hgext:extdiff\"/>." +msgstr "" + +#. type: Content of: <book><chapter><title> +#: ../en/ch13-hgext.xml:5 +msgid "Adding functionality with extensions" +msgstr "使用扩展增加功能" + +#. type: Content of: <book><chapter><para> +#: ../en/ch13-hgext.xml:7 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch13-hgext.xml:12 +msgid "" +"However, Mercurial doesn't box you in with an inflexible command set: you can " +"add features to it as <emphasis>extensions</emphasis> (sometimes known as " +"<emphasis>plugins</emphasis>). We've already discussed a few of these " +"extensions in earlier chapters." +msgstr "" + +#. type: Content of: <book><chapter><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:18 +msgid "" +"<xref linkend=\"sec:tour-merge:fetch\"/> covers the <literal role=\"hg-ext" +"\">fetch</literal> extension; this combines pulling new changes and merging " +"them with local changes into a single command, <command role=\"hg-ext-fetch" +"\">fetch</command>." +msgstr "" + +#. type: Content of: <book><chapter><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:24 +msgid "" +"In <xref linkend=\"chap:hook\"/>, we covered several extensions that are " +"useful for hook-related functionality: <literal role=\"hg-ext\">acl</literal> " +"adds access control lists; <literal role=\"hg-ext\">bugzilla</literal> adds " +"integration with the Bugzilla bug tracking system; and <literal role=\"hg-ext" +"\">notify</literal> sends notification emails on new changes." +msgstr "" + +#. type: Content of: <book><chapter><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:33 +msgid "" +"The Mercurial Queues patch management extension is so invaluable that it " +"merits two chapters and an appendix all to itself. <xref linkend=\"chap:mq\"/" +"> covers the basics; <xref linkend=\"chap:mq-collab\"/> discusses advanced " +"topics; and <xref linkend=\"chap:mqref\"/> goes into detail on each command." +msgstr "" + +#. type: Content of: <book><chapter><para> +#: ../en/ch13-hgext.xml:43 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:48 +msgid "" +"In <xref linkend=\"sec:hgext:inotify\"/>, we'll discuss the possibility of " +"<emphasis>huge</emphasis> performance improvements using the <literal role=" +"\"hg-ext\">inotify</literal> extension." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch13-hgext.xml:55 +msgid "" +"Improve performance with the <literal role=\"hg-ext\">inotify</literal> " +"extension" +msgstr "使用扩展 <literal role=\"hg-ext\">inotify</literal> 以提高性能" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:58 +msgid "" +"Are you interested in having some of the most common Mercurial operations run " +"as much as a hundred times faster? Read on!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:62 +msgid "" +"Mercurial has great performance under normal circumstances. For example, " +"when you run the <command role=\"hg-cmd\">hg status</command> 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 <command role=\"hg-cmd\">hg " +"diff</command> command uses the status machinery to avoid doing an expensive " +"comparison operation on files that obviously haven't changed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:72 +msgid "" +"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 <command role=\"hg-cmd\">hg " +"status</command>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:82 +msgid "" +"To put a number on the magnitude of this effect, I created a repository " +"containing 150,000 managed files. I timed <command role=\"hg-cmd\">hg " +"status</command> as taking ten seconds to run, even when <emphasis>none</" +"emphasis> of those files had been modified." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:88 +msgid "" +"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 <literal>inotify</" +"literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:95 +msgid "" +"Mercurial's <literal role=\"hg-ext\">inotify</literal> extension talks to the " +"kernel's <literal>inotify</literal> component to optimise <command role=\"hg-" +"cmd\">hg status</command> commands. The extension has two components. A " +"daemon sits in the background and receives notifications from the " +"<literal>inotify</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:108 +msgid "" +"Recall the ten seconds that I measured plain Mercurial as taking to run " +"<command role=\"hg-cmd\">hg status</command> on a 150,000 file repository. " +"With the <literal role=\"hg-ext\">inotify</literal> extension enabled, the " +"time dropped to 0.1 seconds, a factor of <emphasis>one hundred</emphasis> " +"faster." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:115 +msgid "Before we continue, please pay attention to some caveats." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:118 +msgid "" +"The <literal role=\"hg-ext\">inotify</literal> extension is Linux-specific. " +"Because it interfaces directly to the Linux kernel's <literal>inotify</" +"literal> subsystem, it does not work on other operating systems." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:123 +msgid "" +"It should work on any Linux distribution that was released after early 2005. " +"Older distributions are likely to have a kernel that lacks <literal>inotify</" +"literal>, or a version of <literal>glibc</literal> that does not have the " +"necessary interfacing support." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:130 +msgid "" +"Not all filesystems are suitable for use with the <literal role=\"hg-ext" +"\">inotify</literal> 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 " +"<literal>inotify</literal> system has no way of knowing about changes made on " +"another system. Most local filesystems (e.g. ext3, XFS, ReiserFS) should " +"work fine." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:141 +msgid "" +"The <literal role=\"hg-ext\">inotify</literal> 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!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:146 +msgid "" +"The extension currently comes in two parts: a set of patches to the Mercurial " +"source code, and a library of Python bindings to the <literal>inotify</" +"literal> subsystem." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch13-hgext.xml:150 +msgid "" +"There are <emphasis>two</emphasis> Python <literal>inotify</literal> binding " +"libraries. One of them is called <literal>pyinotify</literal>, and is " +"packaged by some Linux distributions as <literal>python-inotify</literal>. " +"This is <emphasis>not</emphasis> the one you'll need, as it is too buggy and " +"inefficient to be practical." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:157 +msgid "" +"To get going, it's best to already have a functioning copy of Mercurial " +"installed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><note><para> +#: ../en/ch13-hgext.xml:160 +msgid "" +"If you follow the instructions below, you'll be <emphasis>replacing</" +"emphasis> and overwriting any existing installation of Mercurial that you " +"might already have, using the latest <quote>bleeding edge</quote> Mercurial " +"code. Don't say you weren't warned!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:167 +msgid "" +"Clone the Python <literal>inotify</literal> binding repository. Build and " +"install it." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:174 +msgid "" +"Clone the <filename class=\"directory\">crew</filename> Mercurial " +"repository. Clone the <literal role=\"hg-ext\">inotify</literal> patch " +"repository so that Mercurial Queues will be able to apply patches to your " +"cope of the <filename class=\"directory\">crew</filename> repository." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:184 +msgid "" +"Make sure that you have the Mercurial Queues extension, <literal role=\"hg-ext" +"\">mq</literal>, enabled. If you've never used MQ, read <xref linkend=\"sec:" +"mq:start\"/> to get started quickly." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:190 +msgid "" +"Go into the <filename class=\"directory\">inotify</filename> repo, and apply " +"all of the <literal role=\"hg-ext\">inotify</literal> patches using the " +"<option role=\"hg-ext-mq-cmd-qpush-opt\">hg -a</option> option to the " +"<command role=\"hg-ext-mq\">qpush</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:199 +msgid "" +"If you get an error message from <command role=\"hg-ext-mq\">qpush</command>, " +"you should not continue. Instead, ask for help." +msgstr "" + +#. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> +#: ../en/ch13-hgext.xml:203 +msgid "Build and install the patched version of Mercurial." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:209 +msgid "" +"Once you've build a suitably patched version of Mercurial, all you need to do " +"to enable the <literal role=\"hg-ext\">inotify</literal> extension is add an " +"entry to your <filename role=\"special\">~/.hgrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:214 +msgid "" +"When the <literal role=\"hg-ext\">inotify</literal> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:220 +msgid "" +"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 <literal role=" +"\"hg-ext\">inotify</literal> extension and run a few commands in different " +"repositories, you'll thus see a few <literal>hg</literal> processes sitting " +"around, waiting for updates from the kernel and queries from Mercurial." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:228 +msgid "" +"The first time you run a Mercurial command in a repository when you have the " +"<literal role=\"hg-ext\">inotify</literal> 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, <emphasis>every</emphasis> 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 <literal role=\"hg-ext\">inotify</" +"literal> daemon makes status operations almost instantaneous on repositories " +"of all sizes!" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:242 +msgid "" +"If you like, you can manually start a status daemon using the <command role=" +"\"hg-ext-inotify\">inserve</command> command. This gives you slightly finer " +"control over how the daemon ought to run. This command will of course only " +"be available when the <literal role=\"hg-ext\">inotify</literal> extension is " +"enabled." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:249 +msgid "" +"When you're using the <literal role=\"hg-ext\">inotify</literal> extension, " +"you should notice <emphasis>no difference at all</emphasis> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch13-hgext.xml:260 +msgid "" +"Flexible diff support with the <literal role=\"hg-ext\">extdiff</literal> " +"extension" +msgstr "使用扩展 <literal role=\"hg-ext\">extdiff</literal> 以扩展差异支持" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:263 +msgid "" +"Mercurial's built-in <command role=\"hg-cmd\">hg diff</command> command " +"outputs plaintext unified diffs." +msgstr "" +"Mercurial 内置命令 <command role=\"hg-cmd\">hg diff</command> 的输出与统一差异" +"不同。" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:268 +msgid "" +"If you would like to use an external tool to display modifications, you'll " +"want to use the <literal role=\"hg-ext\">extdiff</literal> extension. This " +"will let you use, for example, a graphical diff tool." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:273 +msgid "" +"The <literal role=\"hg-ext\">extdiff</literal> extension is bundled with " +"Mercurial, so it's easy to set up. In the <literal role=\"rc-extensions" +"\">extensions</literal> section of your <filename role=\"special\">~/.hgrc</" +"filename>, simply add a one-line entry to enable the extension." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:280 +msgid "" +"This introduces a command named <command role=\"hg-ext-extdiff\">extdiff</" +"command>, which by default uses your system's <command>diff</command> command " +"to generate a unified diff in the same form as the built-in <command role=" +"\"hg-cmd\">hg diff</command> command." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:288 +msgid "" +"The result won't be exactly the same as with the built-in <command role=\"hg-" +"cmd\">hg diff</command> variations, because the output of <command>diff</" +"command> varies from one system to another, even when passed the same options." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:293 +msgid "" +"As the <quote><literal>making snapshot</literal></quote> lines of output " +"above imply, the <command role=\"hg-ext-extdiff\">extdiff</command> 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 <command role=\"hg-ext-extdiff\">extdiff</command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:306 +msgid "" +"Snapshot directory names have the same base name as your repository. If your " +"repository path is <filename class=\"directory\">/quux/bar/foo</filename>, " +"then <filename class=\"directory\">foo</filename> 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 " +"<literal>a631aca1083f</literal>, the directory will be named <filename class=" +"\"directory\">foo.a631aca1083f</filename>. A snapshot of the working " +"directory won't have a changeset ID appended, so it would just be <filename " +"class=\"directory\">foo</filename> in this example. To see what this looks " +"like in practice, look again at the <command role=\"hg-ext-extdiff\">extdiff</" +"command> example above. Notice that the diff has the snapshot directory " +"names embedded in its header." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:322 +msgid "" +"The <command role=\"hg-ext-extdiff\">extdiff</command> command accepts two " +"important options. The <option role=\"hg-ext-extdiff-cmd-extdiff-opt\">hg -p</" +"option> option lets you choose a program to view differences with, instead of " +"<command>diff</command>. With the <option role=\"hg-ext-extdiff-cmd-extdiff-" +"opt\">hg -o</option> option, you can change the options that <command role=" +"\"hg-ext-extdiff\">extdiff</command> passes to the program (by default, these " +"options are <quote><literal>-Npru</literal></quote>, which only make sense if " +"you're running <command>diff</command>). In other respects, the <command " +"role=\"hg-ext-extdiff\">extdiff</command> command acts similarly to the built-" +"in <command role=\"hg-cmd\">hg diff</command> command: you use the same " +"option names, syntax, and arguments to specify the revisions you want, the " +"files you want, and so on." +msgstr "" + +# +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:339 +msgid "" +"As an example, here's how to run the normal system <command>diff</command> " +"command, getting it to generate context diffs (using the <option role=\"cmd-" +"opt-diff\">-c</option> option) instead of unified diffs, and five lines of " +"context instead of the default three (passing <literal>5</literal> as the " +"argument to the <option role=\"cmd-opt-diff\">-C</option> option)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:348 +msgid "" +"Launching a visual diff tool is just as easy. Here's how to launch the " +"<command>kdiff3</command> viewer." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:352 +msgid "" +"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 <literal role=\"hg-ext\">mq</literal> extension and the " +"<command>interdiff</command> command, see <xref linkend=\"mq-collab:tips:" +"interdiff\"/>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch13-hgext.xml:360 +msgid "Defining command aliases" +msgstr "定义命令的别名" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch13-hgext.xml:362 +msgid "" +"It can be cumbersome to remember the options to both the <command role=\"hg-" +"ext-extdiff\">extdiff</command> command and the diff viewer you want to use, " +"so the <literal role=\"hg-ext\">extdiff</literal> extension lets you define " +"<emphasis>new</emphasis> commands that will invoke your diff viewer with " +"exactly the right options." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch13-hgext.xml:369 +msgid "" +"All you need to do is edit your <filename role=\"special\">~/.hgrc</" +"filename>, and add a section named <literal role=\"rc-extdiff\">extdiff</" +"literal>. Inside this section, you can define multiple commands. Here's how " +"to add a <literal>kdiff3</literal> command. Once you've defined this, you " +"can type <quote><literal>hg kdiff3</literal></quote> and the <literal role=" +"\"hg-ext\">extdiff</literal> extension will run <command>kdiff3</command> for " +"you." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch13-hgext.xml:379 +msgid "" +"If you leave the right hand side of the definition empty, as above, the " +"<literal role=\"hg-ext\">extdiff</literal> 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 " +"<quote><literal>hg wibble</literal></quote>, which runs <command>kdiff3</" +"command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch13-hgext.xml:389 +msgid "" +"You can also specify the default options that you want to invoke your diff " +"viewing program with. The prefix to use is <quote><literal>opts.</literal></" +"quote>, followed by the name of the command to which the options apply. This " +"example defines a <quote><literal>hg vimdiff</literal></quote> command that " +"runs the <command>vim</command> editor's <literal>DirDiff</literal> extension." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch13-hgext.xml:403 +msgid "" +"Cherrypicking changes with the <literal role=\"hg-ext\">transplant</literal> " +"extension" +msgstr "使用扩展 <literal role=\"hg-ext\">transplant</literal> 以挑选修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:406 +msgid "Need to have a long chat with Brendan about this." +msgstr "" + +#. type: Content of: <book><chapter><sect1><title> +#: ../en/ch13-hgext.xml:410 +msgid "" +"Send changes via email with the <literal role=\"hg-ext\">patchbomb</literal> " +"extension" +msgstr "" +"使用扩展 <literal role=\"hg-ext\">patchbomb</literal> 通过 email 发送修改" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:413 +msgid "" +"Many projects have a culture of <quote>change review</quote>, 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:421 +msgid "" +"Mercurial makes it easy to send changes over email for review or application, " +"via its <literal role=\"hg-ext\">patchbomb</literal> extension. The " +"extension is so named 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 <quote>bombing</quote> the recipient's " +"inbox, hence <quote>patchbomb</quote>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:429 +msgid "" +"As usual, the basic configuration of the <literal role=\"hg-ext\">patchbomb</" +"literal> extension takes just one or two lines in your <filename role=" +"\"special\"> /.hgrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:435 +msgid "" +"Once you've enabled the extension, you will have a new command available, " +"named <command role=\"hg-ext-patchbomb\">email</command>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:439 +msgid "" +"The safest and best way to invoke the <command role=\"hg-ext-patchbomb" +"\">email</command> command is to <emphasis>always</emphasis> run it first " +"with the <option role=\"hg-ext-patchbomb-cmd-email-opt\">hg -n</option> " +"option. This will show you what the command <emphasis>would</emphasis> send, " +"without actually sending anything. Once you've had a quick glance over the " +"changes and verified that you are sending the right ones, you can rerun the " +"same command, with the <option role=\"hg-ext-patchbomb-cmd-email-opt\">hg -n</" +"option> option removed." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:450 +msgid "" +"The <command role=\"hg-ext-patchbomb\">email</command> command accepts the " +"same kind of revision syntax as every other Mercurial command. For example, " +"this command will send every revision between 7 and <literal>tip</literal>, " +"inclusive." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:455 +msgid "" +"You can also specify a <emphasis>repository</emphasis> to compare with. If " +"you provide a repository but no revisions, the <command role=\"hg-ext-" +"patchbomb\">email</command> command will send all revisions in the local " +"repository that are not present in the remote repository. If you " +"additionally specify revisions or a branch name (the latter using the <option " +"role=\"hg-ext-patchbomb-cmd-email-opt\">hg -b</option> option), this will " +"constrain the revisions sent." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:464 +msgid "" +"It's perfectly safe to run the <command role=\"hg-ext-patchbomb\">email</" +"command> 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 " +"<literal>readline</literal>-style editing capabilities when entering those " +"headers, too, which is useful.)" +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:472 +msgid "" +"When you are sending just one revision, the <command role=\"hg-ext-patchbomb" +"\">email</command> command will by default use the first line of the " +"changeset description as the subject of the single email message it sends." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:477 +msgid "" +"If you send multiple revisions, the <command role=\"hg-ext-patchbomb\">email</" +"command> 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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch13-hgext.xml:484 +msgid "Changing the behaviour of patchbombs" +msgstr "修改 patchbomb 的行为" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch13-hgext.xml:486 +msgid "" +"Not every project has exactly the same conventions for sending changes in " +"email; the <literal role=\"hg-ext\">patchbomb</literal> extension tries to " +"accommodate a number of variations through command line options." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:492 +msgid "" +"You can write a subject for the introductory message on the command line " +"using the <option role=\"hg-ext-patchbomb-cmd-email-opt\">hg -s</option> " +"option. This takes one argument, the text of the subject to use." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:498 +msgid "" +"To change the email address from which the messages originate, use the " +"<option role=\"hg-ext-patchbomb-cmd-email-opt\">hg -f</option> option. This " +"takes one argument, the email address to use." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:504 +msgid "" +"The default behaviour is to send unified diffs (see <xref linkend=\"sec:mq:" +"patch\"/> for a description of the format), one per message. You can send a " +"binary bundle instead with the <option role=\"hg-ext-patchbomb-cmd-email-opt" +"\">hg -b</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:512 +msgid "" +"Unified diffs are normally prefaced with a metadata header. You can omit " +"this, and send unadorned diffs, with the <option role=\"hg-ext-patchbomb-cmd-" +"email-opt\">hg --plain</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:518 +msgid "" +"Diffs are normally sent <quote>inline</quote>, in the same body part as the " +"description of a patch. This makes it easiest for the largest number of " +"readers to quote and respond to parts of a diff, as some mail clients will " +"only quote the first MIME body part in a message. If you'd prefer to send the " +"description and the diff in separate body parts, use the <option role=\"hg-" +"ext-patchbomb-cmd-email-opt\">hg -a</option> option." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:528 +msgid "" +"Instead of sending mail messages, you can write them to an <literal>mbox</" +"literal>-format mail folder using the <option role=\"hg-ext-patchbomb-cmd-" +"email-opt\">hg -m</option> option. That option takes one argument, the name " +"of the file to write to." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch13-hgext.xml:535 +msgid "" +"If you would like to add a <command>diffstat</command>-format summary to each " +"patch, and one to the introductory message, use the <option role=\"hg-ext-" +"patchbomb-cmd-email-opt\">hg -d</option> option. The <command>diffstat</" +"command> 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." +msgstr "" diff -r 5981a0f7540a -r 019040fbf5f5 sillybench/sillybench.py --- a/sillybench/sillybench.py Mon Apr 20 23:50:34 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,177 +0,0 @@ -#!/usr/bin/python -# -# Silly benchmarking program, to give a vague idea of how fast a few -# tools are on a handful of common operations. -# -# Use a fairly big and real source tarball to test with: Firefox -# 2.0.0.3 (37622 files, 5374 directories, 343MB unpacked onto -# 4KB-blocksize ext3). - -import csv -import os -import shutil -import sys -import tempfile -import time -import urllib2 - -url = 'ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/2.0.0.3/source/firefox-2.0.0.3-source.tar.bz2' - -class CommandFailure(Exception): - pass - -class rcs(object): - def __init__(self): - self.logfp = open(self.__class__.__name__ + '.csv', 'w') - self.csv = csv.writer(self.logfp) - - def download(self): - name = url[url.rfind('/')+1:] - path = os.path.join(os.environ['HOME'], name) - if not os.path.isfile(path): - ofp = open(path + '.part', 'wb') - try: - ifp = urllib2.urlopen(url) - nbytes = ifp.info()['content-length'] - sys.stdout.write('%s: %s bytes ' % (name, nbytes)) - sys.stdout.flush() - while True: - data = ifp.read(131072) - if not data: break - sys.stdout.write('.') - sys.stdout.flush() - ofp.write(data) - del ofp - os.rename(path + '.part', path) - except: - if os.path.exists(path + '.part'): - os.unlink(path + '.part') - if os.path.exists(path): - os.unlink(path) - raise - return path - - def run(self, args, mustsucceed=True): - ret = os.spawnvp(os.P_WAIT, args[0], args) - if ret < 0: - msg = 'killed by signal %d' % (-ret) - if ret > 0: - msg = 'exited with status %d' % (ret) - if ret: - if mustsucceed: - raise CommandFailure('%s: %s' % (msg, ' '.join(args))) - print >> sys.stderr, 'WARNING: %s: %s' % (msg, ' '.join(args)) - - def time(self, *args, **kwargs): - start = time.time() - self.run(*args, **kwargs) - end = time.time() - return end - start - - def logtime(self, name, elapsed, rest=[]): - self.log('time:' + name, '%.3f' % elapsed, rest) - - def log(self, name, value, rest=[]): - item = (name, value, repr(rest)) - print ' '.join(item) - self.csv.writerow(item) - self.logfp.flush() - - def unpack(self): - tarball = self.download() - t = self.time(['tar', '-C', self.wdir, '-jxf', tarball]) - self.logtime('internal:untar', t) - for name in os.listdir(os.path.join(self.wdir, 'mozilla')): - os.rename(os.path.join(self.wdir, 'mozilla', name), - os.path.join(self.wdir, name)) - - def cleanup(self): - pass - - def add(self, paths): - pass - - def commit(self, msg, paths): - pass - - def status(self, path): - pass - - def remove(self, path): - pass - - -class subversion(rcs): - def __init__(self, root): - rcs.__init__(self) - self.repo = os.path.join(root, 'repo') - self.wdir = os.path.join(root, 'wc') - create = self.time(['svnadmin', 'create', '--fs-type=fsfs', self.repo]) - self.logtime('svn:create', create) - co = self.time(['svn', 'co', 'file://' + self.repo, self.wdir]) - self.logtime('svn:co', co) - self.logtime('init', create + co) - os.chdir(self.wdir) - - def dropmeta(self, names): - return [n for n in names if os.path.basename(n) != '.svn'] - - def add(self, paths): - t = self.time(['svn', 'add', '-q'] + paths) - self.logtime('add %r' % paths, t) - - def commit(self, msg, paths=[]): - if paths: - t = self.time(['svn', 'ci', '-q', '-m', msg] + paths) - else: - t = self.time(['svn', 'ci', '-q', '-m', msg]) - self.logtime('commit %r' % paths, t) - - -class mercurial(rcs): - def __init__(self, root): - rcs.__init__(self) - self.repo = os.path.join(root, 'repo') - self.wdir = self.repo - init = self.time(['hg', 'init', self.repo]) - self.logtime('init', init) - os.chdir(self.wdir) - - def dropmeta(self, names): - return [n for n in names if os.path.basename(n) != '.hg'] - - def add(self, paths): - t = self.time(['hg', 'add', '-q'] + paths) - self.logtime('add %r' % paths, t) - - def commit(self, msg, paths=[]): - if paths: - t = self.time(['hg', 'ci', '-q', '-m', msg] + paths) - else: - t = self.time(['hg', 'ci', '-q', '-m', msg]) - self.logtime('commit %r' % paths, t) - -def benchmark(cls): - oldcwd = os.getcwd() - root = tempfile.mkdtemp(prefix='sillybench.') - try: - print 'root', root - inst = cls(root) - inst.unpack() - names = inst.dropmeta(os.listdir('.')) - dirs = [n for n in names if os.path.isdir(n)] - nondirs = [n for n in names if not os.path.isdir(n)] - dirs.sort(key=hash) - names.sort(key=hash) - for d in dirs[:len(dirs)/2]: - inst.add([d]) - inst.commit('Add %r' % d, [d]) - inst.add(dirs[len(dirs)/2:] + names) - inst.commit('Add remaining dirs and files') - finally: - print >> sys.stderr, '[cleaning up...]' - shutil.rmtree(root) - os.chdir(oldcwd) - -benchmark(mercurial) -#benchmark(subversion) diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/all-ids.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/all-ids.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Prepare an ASCII dump file of all IDs, and the pages in which + they live, for loading into a database. Assumes one-level chunked + HTML output, with each chunk containing either a chapter or + sect1. --> + +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + + <xsl:output method="text"/> + <xsl:strip-space elements="title"/> + + <xsl:template match="/"> + <xsl:for-each select="//preface|//chapter|//appendix|//bibliography|//sect1"> + <xsl:variable name="id"> + <xsl:choose> + <xsl:when test="local-name(.)='sect1'"> + <xsl:value-of select="../@id"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="@id"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable name="sectitle"> + <xsl:value-of select="normalize-space(./title)"/> + </xsl:variable> + <xsl:for-each select=".//para[@id]|.//programlisting[@id]|.//screen[@id]"> + <xsl:value-of select="@id"/> + <xsl:text>|</xsl:text> + <xsl:copy-of select="$id"/> + <xsl:text>|</xsl:text> + <xsl:copy-of select="$sectitle"/> + <xsl:text> </xsl:text> + </xsl:for-each> + </xsl:for-each> + </xsl:template> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/base-html-stylesheet.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/base-html-stylesheet.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,127 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:param name="html.stylesheet">/support/styles.css</xsl:param> + <xsl:param name="toc.section.depth">3</xsl:param> + <xsl:param name="annotate.toc">0</xsl:param> + + <xsl:param name="generate.id.attributes" select="1"></xsl:param> + <xsl:param name="header.rule" select="0"></xsl:param> + <xsl:param name="footer.rule" select="0"></xsl:param> + <xsl:param name="html.cleanup" select="1"></xsl:param> + <xsl:param name="admon.style"><xsl:text></xsl:text></xsl:param> + <xsl:param name="admon.graphics" select="1"></xsl:param> + <xsl:param name="admon.graphics.path">/support/figs/</xsl:param> + + <xsl:template match="sect1" mode="toc"> + <xsl:param name="toc-context" select="."/> + <xsl:call-template name="subtoc"> + <xsl:with-param name="toc-context" select="$toc-context"/> + <xsl:with-param name="nodes" + select="sect2|refentry|bridgehead[$bridgehead.in.toc != 0]"/> + </xsl:call-template> + </xsl:template> + + <xsl:template match="sect2" mode="toc"> + <xsl:param name="toc-context" select="."/> + + <xsl:call-template name="subtoc"> + <xsl:with-param name="toc-context" select="$toc-context"/> + <xsl:with-param name="nodes" + select="sect3|refentry|bridgehead[$bridgehead.in.toc != 0]"/> + </xsl:call-template> + </xsl:template> + + <!-- Add id attributes to <p> tags. This is mostly a copy of the + base XSL. --> + <xsl:template name="paragraph"> + <xsl:param name="class" select="''"/> + <xsl:param name="content"/> + + <xsl:variable name="p"> + <p> + <xsl:call-template name="dir"/> + <xsl:if test="$class != ''"> + <xsl:apply-templates select="." mode="class.attribute"> + <xsl:with-param name="class" select="$class"/> + </xsl:apply-templates> + </xsl:if> + <!-- Here we go. --> + <xsl:if test="$generate.id.attributes != 0"> + <xsl:attribute name="id"> + <xsl:call-template name="object.id"/> + </xsl:attribute> + </xsl:if> + <xsl:copy-of select="$content"/> + </p> + </xsl:variable> + + <xsl:choose> + <xsl:when test="$html.cleanup != 0"> + <xsl:call-template name="unwrap.p"> + <xsl:with-param name="p" select="$p"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:copy-of select="$p"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Add id attributes to <programlisting> and <screen> tags. Once + again, this is mostly a copy of the base XSL, although rather + trimmed down. --> + <xsl:template match="programlisting|screen"> + <xsl:param name="suppress-numbers" select="'0'"/> + + <xsl:call-template name="anchor"/> + + <pre> + <!-- Here we go. --> + <xsl:if test="$generate.id.attributes != 0"> + <xsl:attribute name="id"> + <xsl:call-template name="object.id"/> + </xsl:attribute> + </xsl:if> + + <xsl:apply-templates select="." mode="class.attribute"/> + <xsl:call-template name="apply-highlighting"/> + </pre> + </xsl:template> + + <!-- The default stylesheet generates a little TOC at the beginning + of each qandaset. Uh, no thanks. --> + <xsl:template name="process.qanda.toc"/> + + <xsl:template name="user.header.navigation"> + <div class="navheader"><h2 class="booktitle"><a href="/">Mercurial: The Definitive Guide</a> <span class="authors">by Bryan O'Sullivan</span></h2></div> + </xsl:template> + + <xsl:template name="user.head.content"> + <link rel="alternate" type="application/atom+xml" title="Comments" + href="/feeds/comments/"/> + <link rel="shortcut icon" type="image/png" href="/support/figs/favicon.png"/> + <script type="text/javascript" src="/support/jquery-min.js"></script> + <script type="text/javascript" src="/support/form.js"></script> + <script type="text/javascript" src="/support/hsbook.js"></script> + </xsl:template> + + <xsl:template name="user.footer.content"> + <div class="hgfooter"> + <p><img src="/support/figs/rss.png"/> Want to stay up to date? Subscribe to the comment feed for <a id="chapterfeed" class="feed" href="/feeds/comments/">this chapter</a>, or the <a class="feed" href="/feeds/comments/">entire book</a>.</p> + <p>Copyright 2006, 2007, 2008, 2009 Bryan O'Sullivan. + Icons by <a href="mailto:mattahan@gmail.com">Paul Davey</a> aka <a href="http://mattahan.deviantart.com/">Mattahan</a>.</p> + </div> + </xsl:template> + + <xsl:template name="user.footer.navigation"> + <script type="text/javascript"> + var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); + document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); + </script> + <script type="text/javascript"> + try { + var pageTracker = _gat._getTracker("UA-1805907-5"); + pageTracker._trackPageview(); + } catch(err) {}</script> + </xsl:template> +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/chunk-stylesheet.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/chunk-stylesheet.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,17 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="system-xsl/html/chunk.xsl"/> + <xsl:include href="base-html-stylesheet.xsl"/> + + <!-- PARAMETER REFERENCE: --> + <!-- http://docbook.sourceforge.net/release/xsl/current/doc/html/ --> + + <!-- Uncomment this to enable auto-numbering of sections --> + <!-- xsl:param name="section.autolabel" select="1" / --> + <xsl:param name="chunker.output.encoding">UTF-8</xsl:param> + <xsl:param name="use.id.as.filename" select="1"/> + <xsl:param name="chunk.first.sections" select="0"/> + <xsl:param name="chunk.section.depth" select="0"/> + <xsl:param name="chunk.quietly" select="0"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/dtd-profile.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/dtd-profile.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,15 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + <xsl:import href="system-xsl/profiling/profile.xsl"></xsl:import> + + <!-- For some reason, xsltproc omits the DTD from the file it + outputs. Add a sensible one back in, because otherwise xmllint + won't validate profiled documents. --> + + <xsl:template match="/"> + <xsl:text disable-output-escaping="yes"><![CDATA[ +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" + "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> + ]]></xsl:text> + <xsl:apply-templates select="." mode="profile"/> + </xsl:template> +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/en/fo.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/en/fo.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + version='1.0'> + + <xsl:import href="../fo.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'en'"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/en/html-single.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/en/html-single.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="../html-single.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'en'"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/en/html.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/en/html.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="../html.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'en'"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/fo.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/fo.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/> + + <xsl:param name="draft.mode" select="no"/> + + <!-- These extensions are required for table printing and other stuff --> + <xsl:param name="use.extensions">1</xsl:param> + <xsl:param name="callouts.extension">1</xsl:param> + <xsl:param name="linenumbering.extension">1</xsl:param> + <xsl:param name="tablecolumns.extension">1</xsl:param> + <xsl:param name="textinsert.extension">1</xsl:param> + + <xsl:param name="admon.graphics" select="1" /> + <xsl:param name="admon.graphics.extension">.png</xsl:param> + <xsl:param name="admon.graphics.path">figs/</xsl:param> + <xsl:param name="callout.graphics" select="1" /> + <xsl:param name="callout.graphics.extension">.png</xsl:param> + <xsl:param name="callout.graphics.path">images/callouts/</xsl:param> + + <xsl:param name="section.autolabel" select="1" /> + <xsl:param name="section.label.includes.component.label">1</xsl:param> + + <xsl:param name="variablelist.as.blocks" select="1" /> <!-- fo only --> + <xsl:param name="hyphenate">false</xsl:param> <!-- fo only --> + <xsl:param name="paper.type" select="'A4'"></xsl:param> <!-- fo only --> + + <!-- Default font settings --> + <!-- + <xsl:param name="title.font.family">sans-serif</xsl:param> + <xsl:param name="body.font.family">serif</xsl:param> + <xsl:param name="sans.font.family">sans-serif</xsl:param> + <xsl:param name="dingbat.font.family">serif</xsl:param> + <xsl:param name="monospace.font.family">monospace</xsl:param> + <xsl:param name="symbol.font.family">Symbol,ZapfDingbats</xsl:param> + --> + + <!-- Custom font settings - preferred truetype font --> + <xsl:param name="title.font.family">Calibri,sans-serif,SimHei</xsl:param> + <xsl:param name="body.font.family">Cambria,Cambria Math,serif,SimSun</xsl:param> + <xsl:param name="sans.font.family">Calibri,sans-serif,SimHei</xsl:param> + <xsl:param name="dingbat.font.family">Cambria,Cambria Math,serif,SimSun</xsl:param> + <xsl:param name="monospace.font.family">Courier New,monospace,FangSong</xsl:param> + + <!-- Page related Settings --> + <xsl:param name="page.margin.inner">1.5cm</xsl:param> + <xsl:param name="page.margin.outer">1.5cm</xsl:param> + <xsl:param name="title.margin.left">0pt</xsl:param> + <xsl:param name="body.start.indent">24pt</xsl:param> + <xsl:param name="body.end.indent">0pt</xsl:param> + + <!-- Breaking long lines --> + <xsl:param name="hyphenate.verbatim">0</xsl:param> + <xsl:attribute-set name="monospace.verbatim.properties" + use-attribute-sets="verbatim.properties monospace.properties"> + <xsl:attribute name="wrap-option">wrap</xsl:attribute> + <xsl:attribute name="hyphenation-character">►</xsl:attribute> + </xsl:attribute-set> + + <!-- Prevent blank pages in output --> + <xsl:template name="book.titlepage.before.verso"> + </xsl:template> + <xsl:template name="book.titlepage.verso"> + </xsl:template> + <xsl:template name="book.titlepage.separator"> + </xsl:template> + + <!-- Colourize links in output --> + <xsl:attribute-set name="xref.properties"> + <xsl:attribute name="color"> + <xsl:choose> + <xsl:when test="self::ulink">blue</xsl:when> + <xsl:when test="self::xref">blue</xsl:when> + <xsl:when test="self::uri">blue</xsl:when> + <xsl:otherwise>red</xsl:otherwise> + </xsl:choose> + </xsl:attribute> + </xsl:attribute-set> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/hgbook.css Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,295 @@ +body +{ + background: white; + margin: 1in; + font-family: Georgia,SimSun,serif; +} + +p, li, ul, ol, dd, dt +{ + font-style: normal; + font-weight: normal; + color: black; +} + +tt, pre +{ + font-family: Consolas,KaiTi,FangSong,SimSun,monospace; +} + +a +{ + color: blue; + text-decoration: underline; +} + +a:hover +{ + background: rgb(75%,75%,100%); + color: blue; + text-decoration: underline; +} + +a:visited +{ + color: purple; + text-decoration: underline; +} + +img +{ + border: none; +} + +h1.title +{ + font-size: 250%; + font-style: normal; + font-weight: bold; + color: black; +} + +h2.subtitle +{ + font-size: 150%; + font-style: italic; + color: black; +} + +h2.title +{ + font-size: 150%; + font-style: normal; + font-weight: bold; + color: black; +} + +h3.title +{ + font-size: 125%; + font-style: normal; + font-weight: bold; + color: black; +} + +h4.title +{ + font-size: 100%; + font-style: normal; + font-weight: bold; + color: black; +} + +strong +{ + font-weight: normal; +} + +.toc b +{ + font-family: Verdana,SimHei,sans-serif; + font-size: 120%; + font-style: normal; + font-weight: bold; + color: black; +} + +.title +{ + font-family: Verdana,SimHei,sans-serif; +} + +.screen, .programlisting, .structname +{ + font-family: Consolas,KaiTi,FangSong,SimSun,monospace; + font-style: normal; + font-weight: normal; +} + +.userinput +{ + font-weight: normal; +} + +.command +{ + font-style: italic; +} + +.filename +{ + font-family: Georgia,SimSun,serif; + font-style: italic; +} + +.figure, .example, .table +{ + margin: 0.125in 0.25in; +} + +.figure p.title b, .example p.title b, .table p.title b +{ + font-family: Georgia,SimSun,serif; + font-size: 80%; + font-style: italic; + font-weight: normal; +} + +.table table +{ + border-width: 1px; + border-style: solid; + border-color: black; + border-spacing: 0; + background: rgb(240,240,240); +} + +.table td +{ + border: none; + border-right: 1px black solid; + border-bottom: 1px black solid; + padding: 2px; +} + +.table th +{ + background: rgb(180,180,180); + border: none; + border-right: 1px black solid; + border-bottom: 1px black solid; + padding: 2px; +} + +.table p.title, .figure p.title, .example p.title +{ + text-align: left !important; + font-size: 100% !important; +} + +.author, .pubdate +{ + margin: 0; + font-size: 100%; + font-style: italic; + font-weight: normal; + color: black; +} + +.preface div.author, .preface .pubdate +{ + font-size: 80%; +} + +.sidebar +{ + border-top: dotted 1px black; + border-left: dotted 1px black; + border-right: solid 2px black; + border-bottom: solid 2px black; + background: rgb(240,220,170); + padding: 0 0.12in; + margin: 0.25in; +} + +.note .programlisting, .note .screen, +.tip .programlisting, .tip .screen, +.warning .programlisting, .warning .screen, +.sidebar .programlisting, .sidebar .screen +{ + border: none; + background: none; +} + +.sidebar p.title +{ + text-align: center; + font-size: 125%; +} + +.note, .tip, .warning +{ + border: black solid 1px; + margin: 0.125in 0; + padding: 0 55px; + font-size: 90%; +} + +/* +.note +{ + background: url(./figs/note.png) no-repeat rgb(252,246,220); +} + +.tip +{ + background: url(./figs/tip.png) no-repeat rgb(224,244,255); +} + +.warning +{ + background: url(./figs/warning.png) no-repeat rgb(255,210,210); +} +*/ + +.note .title, .tip .title, .warning .title +{ + display: none; +} + +.programlisting, .screen +{ + font-size: 90%; + color: black; + margin: 1em 0.25in; + padding: 0.5em; + background: rgb(240,240,240); + border-top: black dotted 1px; + border-left: black dotted 1px; + border-right: black solid 2px; + border-bottom: black solid 2px; +} + +.navheader, .navfooter +{ + border: black solid 1px; + background: rgb(180,180,200); +} + +.navheader hr, .navfooter hr +{ + display: none; +} + +#svn-footer +{ + font-size: 80%; + text-align: center; +} + +#svn-footer hr +{ + display: none; +} + +/* --------------------- */ +/* PRINT MEDIA OVERRIDES */ +/* --------------------- */ + +@media print +{ + body + { + margin: 0; + } + + .navheader, .navfooter + { + display: none; + } + + #svn-footer hr + { + display: block; + } +} diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/html-single.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/html-single.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/> + + <xsl:param name="draft.mode" select="no"/> + + <!-- xsltproc can't support these extensions + <xsl:param name="use.extensions">1</xsl:param> + <xsl:param name="callouts.extension">1</xsl:param> + <xsl:param name="linenumbering.extension">1</xsl:param> + <xsl:param name="tablecolumns.extension">1</xsl:param> + <xsl:param name="textinsert.extension">1</xsl:param> + --> + + <xsl:param name="admon.graphics" select="1" /> + <xsl:param name="admon.graphics.extension">.png</xsl:param> + <xsl:param name="admon.graphics.path">figs/</xsl:param> + <xsl:param name="callout.graphics" select="1" /> + <xsl:param name="callout.graphics.extension">.png</xsl:param> + <xsl:param name="callout.graphics.path">images/callouts/</xsl:param> + + <xsl:param name="section.autolabel" select="1" /> + <xsl:param name="section.label.includes.component.label">1</xsl:param> + + <xsl:output method="html" encoding="utf-8" indent="yes"/> <!-- html only --> + <xsl:param name="use.id.as.filename">0</xsl:param> <!-- html only --> + <xsl:param name="chunk.section.depth">0</xsl:param> <!-- html only --> + <xsl:param name="chunker.output.indent">yes</xsl:param> <!-- html only --> + <xsl:param name="html.stylesheet">hgbook.css</xsl:param> <!-- html only --> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/html.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/html.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/> + + <xsl:param name="draft.mode" select="no"/> + + <!-- xsltproc can't support these extensions + <xsl:param name="use.extensions">1</xsl:param> + <xsl:param name="callouts.extension">1</xsl:param> + <xsl:param name="linenumbering.extension">1</xsl:param> + <xsl:param name="tablecolumns.extension">1</xsl:param> + <xsl:param name="textinsert.extension">1</xsl:param> + --> + + <xsl:param name="admon.graphics" select="1" /> + <xsl:param name="admon.graphics.extension">.png</xsl:param> + <xsl:param name="admon.graphics.path">figs/</xsl:param> + <xsl:param name="callout.graphics" select="1" /> + <xsl:param name="callout.graphics.extension">.png</xsl:param> + <xsl:param name="callout.graphics.path">images/callouts/</xsl:param> + + <xsl:param name="section.autolabel" select="1" /> + <xsl:param name="section.label.includes.component.label">1</xsl:param> + + <xsl:output method="html" encoding="utf-8" indent="yes"/> <!-- html only --> + <xsl:param name="chunker.output.encoding" select="'utf-8'"/> <!-- html only --> + <xsl:param name="chunker.output.indent" select="'yes'"/> <!-- html only --> + <xsl:param name="use.id.as.filename">0</xsl:param> <!-- html only --> + <xsl:param name="chunk.section.depth">0</xsl:param> <!-- html only --> + <xsl:param name="chunker.output.indent">yes</xsl:param> <!-- html only --> + <xsl:param name="html.stylesheet">hgbook.css</xsl:param> <!-- html only --> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/zh/fo.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/zh/fo.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + version='1.0'> + + <xsl:import href="../fo.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'zh'"/> + + <!-- Chinese font related settings --> + <xsl:param name="body.font.master">12</xsl:param> + + <xsl:attribute-set name="standard.para.spacing" use-attribute-sets="normal.para.spacing"> + <xsl:attribute name="text-indent">24pt</xsl:attribute> + </xsl:attribute-set> + + <xsl:template match="abstract/para|appendix/para|chapter/para|colophon/para|legalnotice/para|preface/para|section/para|sect1/para|sect2/para"> + <fo:block xsl:use-attribute-sets="standard.para.spacing"> + <xsl:call-template name="anchor"/> + <xsl:apply-templates/> + </fo:block> + </xsl:template> + + <xsl:template match="section/para/*"> + <fo:wrapper text-indent="0pt"> + <xsl:apply-imports/> + </fo:wrapper> + </xsl:template> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/zh/html-single.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/zh/html-single.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="../html-single.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'zh'"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 stylesheets/zh/html.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stylesheets/zh/html.xsl Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'> + + <xsl:import href="../html.xsl"/> + + <xsl:param name="l10n.gentext.language" select="'zh'"/> + +</xsl:stylesheet> diff -r 5981a0f7540a -r 019040fbf5f5 web/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/README Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,5 @@ +This directory contains web-related files. Surprise! + +javascript - files used by the comment system, based on jQuery +hgbook - Django app that acts as the comment back end +styles.css - style file diff -r 5981a0f7540a -r 019040fbf5f5 web/genindex.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/genindex.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import glob, os, re + +chapter_re = re.compile(r'<(chapter|appendix|preface)\s+id="([^"]+)">') +filename_re = re.compile(r'<\?dbhtml filename="([^"]+)"\?>') +title_re = re.compile(r'<title>(.*)') + +chapters = glob.glob('../en/ch*.xml') + glob.glob('../en/app*.xml') + +fp = open('index-read.html.in', 'w') + +print >> fp, ''' +{% extends "boilerplate.html" %} +{% block bodycontent %} + +
      ''' + +ch = 0 +app = 0 +ab = 0 +for c in chapters: + filename = None + title = None + chapid = None + chaptype = None + for line in open(c): + m = chapter_re.search(line) + if m: + chaptype, chapid = m.groups() + m = filename_re.search(line) + if m: + filename = m.group(1) + m = title_re.search(line) + if m: + title = m.group(1) + if filename and title and chapid: + if chaptype == 'appendix': + num = chr(ord('A') + app) + app += 1 + else: + num = ch + ch += 1 + ab += 1 + date = os.popen('hg log -l1 --template "{date|isodate}" ' + c).read().split(None, 1)[0] + args = { + 'ab': "ab"[ab % 2], + 'date': date, + 'chapid': chapid, + 'num': num, + 'filename': filename, + 'title': title, + } + print >> fp, '
    • %(date)s%(num)s. %(title)s
    • ' % args + break + +print >> fp, '''
    +{% endblock %}''' + +fp.close() diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook.conf Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,101 @@ +# -*- apache -*- + + + ServerName hgbook.red-bean.com + ServerAdmin bos@serpentine.com + ErrorLog logs/hgbook-error_log + # Debian: + # CustomLog logs/hgbook-access_log full + # Fedora: + CustomLog logs/hgbook-access_log combined + Options +MultiViews + DirectoryIndex index.html.var index.html + DocumentRoot "/home/bos/hg/hgbook/en/html" + + Redirect permanent /hgbook.html /index.html + Redirect permanent /hgbookch1.html /read/preface.html + Redirect permanent /hgbookch2.html /read/a-tour-of-mercurial-the-basics.html + Redirect permanent /hgbookch3.html /read/a-tour-of-mercurial-merging-work.html + Redirect permanent /hgbookch4.html /read/behind-the-scenes.html + Redirect permanent /hgbookch5.html /read/mercurial-in-daily-use.html + Redirect permanent /hgbookch6.html /read/file-names-and-pattern-matching.html + Redirect permanent /hgbookch6.html /read/managing-releases-and-branchy-development.html + Redirect permanent /hgbookch7.html /read/finding-and-fixing-mistakes.html + Redirect permanent /hgbookch8.html /read/handling-repository-events-with-hooks.html + Redirect permanent /hgbookch9.html /read/customizing-the-output-of-mercurial.html + Redirect permanent /hgbookch10.html /read/managing-change-with-mercurial-queues.html + Redirect permanent /hgbookch11.html /read/advanced-uses-of-mercurial-queues.html + Redirect permanent /hgbookch12.html /read/adding-functionality-with-extensions.html + Redirect permanent /hgbookap1.html /read/command-reference.html + Redirect permanent /hgbookap2.html /read/mercurial-queues-reference.html + Redirect permanent /hgbookap3.html /read/installing-mercurial-from-source.html + Redirect permanent /hgbookap4.html /read/open-publication-license.html + Redirect permanent /hgbookli1.html /read/index.html + Redirect permanent /hgbookli2.html /read/index.html + Redirect permanent /hgbookli3.html /read/index.html + Redirect permanent /hgbookli4.html /read/index.html + + # Actively redirect requests via a ServerAlias to the canonical hostname. + RewriteEngine On + RewriteCond %{HTTP_HOST} !=hgbook.red-bean.com + RewriteRule ^(.*) http://hgbook.red-bean.com$1 [R] + + + SetHandler python-program + # hg clone http://bitbucket.org/mirror/django-trunk/ + PythonPath "['/home/bos/hg/django-trunk', '/home/bos/hg/hgbook/web'] + sys.path" + PythonHandler django.core.handlers.modpython + PythonAutoReload Off + SetEnv DJANGO_SETTINGS_MODULE hgbook.settings + PythonDebug Off + + + + SetHandler None + DirectoryIndex index.html + + + + SetHandler None + + + + SetHandler None + + + + SetHandler None + + + + SetHandler None + + + + SetHandler None + + + Alias /media /home/bos/hg/django-trunk/django/contrib/admin/media + + + Options Indexes FollowSymlinks + AllowOverride None + Order allow,deny + Allow from all + + + + AllowOverride AuthConfig + + + + Options None + + + + + Options None + AllowOverride None + Order allow,deny + Allow from all + diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/__init__.py diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/admin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/admin.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,23 @@ +from django.contrib import admin +from hgbook.comments.models import Comment, Element + +class CommentAdmin(admin.ModelAdmin): + list_display = ['element', 'submitter_name', 'comment', 'reviewed', + 'hidden', 'date'] + search_fields = ['comment'] + date_hierarchy = 'date' + list_filter = ['date', 'submitter_name'] + search_fields = ['title', 'submitter_name', 'submitter_url'] + fields = ( + (None, {'fields': ('submitter_name', 'element', 'comment')}), + ('Review and presentation state', + {'fields': ('reviewed', 'hidden')}), + ('Other info', {'fields': ('date', 'submitter_url', 'ip')}), + ) + +class ElementAdmin(admin.ModelAdmin): + search_fields = ['id', 'chapter'] + list_filter = ['chapter', 'title'] + +admin.site.register(Comment, CommentAdmin) +admin.site.register(Element, ElementAdmin) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/__init__.py diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/feeds.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/feeds.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,35 @@ +from django.core.exceptions import ObjectDoesNotExist +from django.utils.feedgenerator import Atom1Feed +from django.contrib.syndication.feeds import Feed +from hgbook.comments.models import Comment, Element + +class MyAtomFeed(Atom1Feed): + title_type = u'html' + +class Comments(Feed): + feed_type = MyAtomFeed + title = 'Mercurial - The Definitive Guide: recent comments' + subtitle = ('Recent comments on the text of “Mercurial: The ' + 'Definitive Guide”, from our readers') + link = '/feeds/comments/' + author_name = 'Our readers' + + def feedfilter(self, queryset): + return queryset.order_by('-date')[:20] + + def items(self): + return self.feedfilter(Comment.objects) + + def item_author_name(self, obj): + return obj.submitter_name + + def item_pubdate(self, obj): + return obj.date + + def get_object(self, bits): + if len(bits) == 0: + return self.items() + elif len(bits) > 1: + raise ObjectDoesNotExist + return self.feedfilter(Comment.objects.filter(element__chapter=bits[0], + hidden=False)) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/models.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/models.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,44 @@ +from django.db import models +import sha + +mutable = True + +class Element(models.Model): + id = models.CharField('ID attribute', max_length=64, editable=False, + primary_key=True) + chapter = models.CharField('Chapter ID', max_length=64, editable=False, + db_index=True) + title = models.CharField('Section title', max_length=256, editable=False) + + def __unicode__(self): + return self.id + +class Comment(models.Model): + element = models.ForeignKey(Element, + help_text='ID of paragraph that was commented on') + comment = models.TextField(editable=mutable, + help_text='Text of submitted comment (please do not modify)') + submitter_name = models.CharField('Submitter', max_length=64, + help_text='Self-reported name of submitter (may be bogus)') + submitter_url = models.URLField('URL', blank=True, editable=mutable, + help_text='Self-reported URL of submitter (may be empty or bogus)') + ip = models.IPAddressField('IP address', editable=mutable, + help_text='IP address from which comment was submitted') + date = models.DateTimeField('date submitted', auto_now=True, + auto_now_add=True) + reviewed = models.BooleanField(default=False, db_index=True, + help_text='Has this comment been reviewed by an author?') + hidden = models.BooleanField(default=False, db_index=True, + help_text='Has this comment been hidden from public display?') + + def __unicode__(self): + return self.comment[:32] + + def get_absolute_url(self): + s = sha.new() + s.update(repr(self.comment)) + s.update(repr(self.submitter_name)) + s.update(str(self.date)) + return '/read/%s.html#%s?comment=%s&uuid=%s' % ( + self.element.chapter, self.element.id, self.id, s.hexdigest()[:20] + ) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/sql/comment.mysql.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/sql/comment.mysql.sql Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,2 @@ +alter table comments_comment convert to character set utf8 collate utf8_bin; +alter table comments_comment default character set utf8 collate utf8_bin; diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/sql/element.mysql.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/sql/element.mysql.sql Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,2 @@ +alter table comments_element convert to character set utf8 collate utf8_bin; +alter table comments_element default character set utf8 collate utf8_bin; diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/urls.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/urls.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('', + (r'chapter/(?P[^/]+)/?$', 'hgbook.comments.views.chapter'), + (r'chapter/(?P[^/]+)/count/?$', 'hgbook.comments.views.chapter_count'), + (r'single/(?P[^/]+)/?$', 'hgbook.comments.views.single'), + (r'submit/(?P[^/]+)/?$', 'hgbook.comments.views.submit') +) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/comments/views.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/comments/views.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,101 @@ +import django.forms as forms +from django.db import connection +from django.http import HttpResponse +from hgbook.comments.models import Comment, Element +from django.shortcuts import get_object_or_404, render_to_response +from django.template import Context +from django.template.loader import get_template +from django.utils.simplejson import dumps + +def dump_queries(): + # requires settings.DEBUG to be set to True in order to work + if len(connection.queries) == 1: + print connection.queries + else: + qs = {} + for q in connection.queries: + qs[q['sql']] = qs.setdefault(q['sql'], 0) + 1 + for q in sorted(qs.items(), key=lambda x: x[1], reverse=True): + print q + print len(connection.queries) + +class CommentForm(forms.Form): + id = forms.CharField(widget=forms.HiddenInput) + name = forms.CharField(max_length=64) + url = forms.URLField(max_length=128, required=False) + comment = forms.CharField(widget=forms.Textarea(attrs={ + 'rows': 8, 'cols': 60 + })) + remember = forms.BooleanField(initial=True, required=False) + +def comments_by_chapter(id): + objs = {} + for c in Comment.objects.filter(element__chapter=id, hidden=False).order_by('date'): + objs.setdefault(c.element_id, []).append(c) + return objs + +def chapter(request, id): + template = get_template('comment.html') + resp = {} + for elt, comments in comments_by_chapter(id).iteritems(): + form = CommentForm(initial={ + 'id': elt, + 'name': request.session.get('name', ''), + }) + resp[elt] = template.render(Context({ + 'id': elt, + 'form': form, + 'length': len(comments), + 'query': comments, + })) + return HttpResponse(dumps(resp), mimetype='application/json') + +def chapter_count(request, id): + resp = comments_by_chapter(id) + for elt, comments in resp.iteritems(): + resp[elt] = len(comments) + return HttpResponse(dumps(resp), mimetype='application/json') + +def single(request, id, form=None, newid=None): + queryset = Comment.objects.filter(element=id, hidden=False).order_by('date') + if form is None: + form = CommentForm(initial={ + 'id': id, + 'name': request.session.get('name', ''), + }) + try: + error = form.errors[0] + except: + error = '' + return render_to_response('comment.html', { + 'id': id, + 'form': form, + 'length': len(queryset), + 'query': queryset, + 'newid': newid or True, + 'error': error, + }) + +def submit(request, id): + element = get_object_or_404(Element, id=id) + form = None + newid = None + if request.method == 'POST': + form = CommentForm(request.POST) + if form.is_valid(): + data = form.cleaned_data + if data.get('remember'): + request.session['name'] = data['name'] + request.session['url'] = data['url'] + else: + request.session.pop('name', None) + request.session.pop('url', None) + c = Comment(element=element, + comment=data['comment'], + submitter_name=data['name'], + submitter_url=data['url'], + ip=request.META.get('REMOTE_ADDR')) + c.save() + newid = c.id + form = None + return single(request, id, form, newid) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/dbutil.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/dbutil.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,32 @@ +import MySQLdb as mysql +import sys + +def connect(): + try: + import secrets + except ImportError: + print >> sys.stderr, 'Decrypt secrets.py.gpg or create a new copy!' + sys.exit(1) + + if secrets.DATABASE_ENGINE != 'mysql': + print >> sys.stderr, ('You are using a %s database' % + secrets.DATABASE_ENGINE) + sys.exit(1) + + kwargs = { + 'charset': 'utf8', + 'use_unicode': True, + } + if secrets.DATABASE_USER: + kwargs['user'] = secrets.DATABASE_USER + if secrets.DATABASE_NAME: + kwargs['db'] = secrets.DATABASE_NAME + if secrets.DATABASE_PASSWORD: + kwargs['passwd'] = secrets.DATABASE_PASSWORD + if secrets.DATABASE_HOST.startswith('/'): + kwargs['unix_socket'] = secrets.DATABASE_HOST + elif secrets.DATABASE_HOST: + kwargs['host'] = secrets.DATABASE_HOST + if secrets.DATABASE_PORT: + kwargs['port'] = int(secrets.DATABASE_PORT) + return mysql.connect(**kwargs) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/load_elements.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/load_elements.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# +# This script updates the contents of the comments_element table. +# It's fugly, but a lot less painful than trying to use Django's +# fixtures system. + +import os, sys +sys.path.append(os.path.dirname(__file__)) +import dbutil + +os.system('make -C ../../en all-ids.dat') + +conn = dbutil.connect() +c = conn.cursor() +c.execute('''load data local infile "../../en/all-ids.dat" replace + into table comments_element + fields terminated by "|"''') +print 'Database updated' diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/manage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/manage.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,11 @@ +#!/usr/bin/env python +from django.core.management import execute_manager +try: + import settings # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) + sys.exit(1) + +if __name__ == "__main__": + execute_manager(settings) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/reviewers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/reviewers.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os, sys +sys.path.append(os.path.dirname(__file__)) +import dbutil + +conn = dbutil.connect() +c = conn.cursor() + +c.execute('''select submitter_name from comments_comment''') + +reviewers = {} + +mappings = { + u'alejandro "tab-lover" dubrovsky': u'Alejandro Dubrovsky', + u'alex hirzel ': u'Alex Hirzel', + u'anonymous coward': u'Anonymous', + u'arthur van leeuwen': u'Arthur van Leeuwen', + u'augustss': u'Lennart Augustsson', + u'ed t': u'Anonymous', + u'geogre moschovitis': u'George Moschovitis', + u'george m': u'George Moschovitis', + u'haskell newb': u'Anonymous', + u'j. pablo fernandez': u'J. Pablo Fernández', + u'kamal al-marhoobi': u'Kamal Al-Marhubi', + u'kevin w.': u'Kevin Watters', + u'max cantor (#haskell - mxc)': u'Max Cantor', + u'michael campbell': u'Michael Campbell', + u'mike btauwerman': u'Mike Brauwerman', + u'no credit necessary': u'Anonymous', + u'nykänen, matti': u'Matti Nykänen', + u'omar antolin camarena': u'Omar Antolín Camarena', + u'ryan t mulligan': u'Ryan T. Mulligan', + u'sengan baring-gould': u'Sengan Baring-Gould', + u'some guy': u'Anonymous', + u'tomas janousek': u'Tomáš Janoušek', + u'william halchin': u'William N. Halchin', + } + +def fixup(s): + try: + return s.encode('ascii') + except UnicodeEncodeError: + def f(c): + o = ord(c) + if o < 128: + return c + return '&#%d;' % o + return ''.join(map(f, s)) + +total = 0 +for r in c.fetchall(): + r = r[0].decode('utf-8') + if r in ("Bryan O'Sullivan",): + continue + total += 1 + m = mappings.get(r.lower()) + if m: + r = m + elif len(r) < 2 or ' ' not in r: + r = 'Anonymous' + reviewers.setdefault(r, 0) + reviewers[r] += 1 + +reviewers = sorted(reviewers.iteritems(), key=lambda x: x[0]) + +cohorts = [(.01,1),(.002,.01)] + +for (lo,hi) in cohorts: + lo = total * lo + hi = total * hi + for r in [n for n in reviewers if lo <= n[1] < hi]: + if r[1] > 3: + print '%s,' % fixup(r[0]) + print + +lo = total * .002 +for n in reviewers: + if n[1] < lo: + print '%s,' % fixup(n[0]) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/secrets.py.gpg Binary file web/hgbook/secrets.py.gpg has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/settings.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/settings.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,87 @@ +# Django settings for hgbook project. + +import os, sys + +DEBUG = False +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ("Bryan O'Sullivan", 'bos@serpentine.com'), +) + +MANAGERS = ADMINS + +ROOT = os.path.dirname(sys.modules[__name__].__file__) + +try: + from secrets import DATABASE_ENGINE, DATABASE_NAME, DATABASE_USER, \ + DATABASE_PASSWORD, DATABASE_HOST, DATABASE_PORT, SECRET_KEY +except ImportError: + print >> sys.stderr, 'Faking up some database configuration for you' + DATABASE_ENGINE = 'sqlite3' + DATABASE_NAME = os.path.join(ROOT, '.database.sqlite3') + DATABASE_USER = '' + DATABASE_PASSWORD = '' + DATABASE_HOST = '' + DATABASE_PORT = '' + SECRET_KEY = '' + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be avilable on all operating systems. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Los_Angeles' + +# Language code for this installation. All choices can be found here: +# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/media/' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', +# 'django.template.loaders.eggs.load_template_source', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.middleware.doc.XViewMiddleware', +) + +ROOT_URLCONF = 'hgbook.urls' + +TEMPLATE_DIRS = ( + os.path.join(ROOT, 'templates') +) + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'hgbook.comments', +) diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/404.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/404.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,8 @@ +{% extends "simple.html" %} + +{% block title %}Page Not Found{% endblock %} + +{% block body %} +

    Sorry, we hit when trying to find the +page you requested.

    +{% endblock %} diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/500.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/500.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,11 @@ +{% extends "simple.html" %} + +{% block title %}Internal Server Error{% endblock %} + +{% block body %} +

    Sorry, we hit when +trying to process your request. If possible, please let Bryan know that this problem happened, +and what you were doing when it occurred.

    +{% endblock %} diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/boilerplate.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/boilerplate.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,37 @@ + + + + {% block pagetitle %}Mercurial: The Definitive Guide{% endblock %} + + + + + + + + + + {% block bodycontent %}{% endblock %} + +

    Want to stay + up to date? Subscribe to comment feeds for any chapter, or + the entire book.

    Copyright + 2006, 2007, 2008, 2009 Bryan O'Sullivan. + Icons by + Paul Davey aka Mattahan.

    +
    + + + + + diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/comment.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/comment.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,62 @@ +{% ifequal length 1 %} + One comment +{% else %} + {% if length %} + {{ length }} comments + {% else %} + No comments + + {% endif %} +{% endifequal %} +{% for c in query %} + +{% endfor %} + diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/feeds/comments_description.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/feeds/comments_description.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,12 @@ +

    On {{ obj.date|date:"Y-m-d" }}, + {% if obj.submitter_url %} + {{ obj.submitter_name|escape }} + {% else %} + {{ obj.submitter_name|escape }} + {% endif %} +commented on “{{ obj.element.title|escape }}”:

    +
    +{{ obj.comment|escape|linebreaks }} +
    +

    To see this comment in context or to respond, visit {{ site.domain }}

    diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/feeds/comments_title.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/feeds/comments_title.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +Comment on “{{ obj.element.title|escape }}” diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/templates/simple.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/templates/simple.html Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,7 @@ +{% extends "boilerplate.html" %} + +{% block bodycontent %} + + +
    {% block body %}{% endblock %}
    +{% endblock %} diff -r 5981a0f7540a -r 019040fbf5f5 web/hgbook/urls.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/hgbook/urls.py Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,25 @@ +import os +from django.conf.urls.defaults import * +import hgbook.comments.feeds as feeds +from django.contrib import admin + +admin.autodiscover() + +feeds = { + 'comments': feeds.Comments, + } + +urlpatterns = patterns('', + (r'^comments/', include('hgbook.comments.urls')), + + (r'^feeds/(?P.*)/$', 'django.contrib.syndication.views.feed', + {'feed_dict': feeds}), + + # Only uncomment this for local testing without Apache. + # (r'^html/(?P.*)$', 'django.views.static.serve', + # {'document_root': os.path.realpath(os.path.dirname( + # sys.modules[__name__].__file__) + '/../../en/html'), + + # Uncomment this for admin: + (r'^admin/(.*)', admin.site.root), +) diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/caution.png Binary file web/icons/caution.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/favicon.png Binary file web/icons/favicon.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/important.png Binary file web/icons/important.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/note.png Binary file web/icons/note.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/remark.png Binary file web/icons/remark.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/rss.png Binary file web/icons/rss.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/shell.png Binary file web/icons/shell.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/source.png Binary file web/icons/source.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/tip.png Binary file web/icons/tip.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/icons/warning.png Binary file web/icons/warning.png has changed diff -r 5981a0f7540a -r 019040fbf5f5 web/index.html.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/index.html.in Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,65 @@ + +{% extends "boilerplate.html" %} + +{% block bodycontent %} + + +
    +

    Welcome to Mercurial: The Definitive Guide

    + +

    This is the online home of the book “Mercurial: The + Definitive Guide”. + It will be published in 2009 by O'Reilly Media.

    + +

    I make the content freely available + online. If you like it, please make a note to buy a copy!

    + +

    For news updates, please + visit my blog.

    + +

    You can contribute!

    + +

    I publish the source + code for this book as a Mercurial repository. Please feel + welcome to clone it, make modifications to your copy, and send me + changes.

    + +

    The online version of the book includes a comment system + that you can use to send feedback involving errors, omissions, and + suggestions.

    + +

    (If you would like to adapt the comment system for a + publishing project of your own, the source for the web application + is included with the book source at the link above.)

    + +

    What is Mercurial?

    + +

    Mercurial is a + fast, lightweight source control management system + designed for easy and efficient handling of very large distributed + projects.

    + +

    How I help Mercurial and free software, and you can too

    + +

    Mercurial is a member of the Software Freedom Conservancy, a + wonderful non-profit organisation that offers its member projects + legal and administrative advice.

    + +

    I am donating my royalties from the sales of this book (once it is + published) to the Software Freedom Conservancy, and I encourage + you to support their work, too.

    + +

    The SFC can + accept accept + donations (tax-free under IRS 501(c)(3), within the United + States) on behalf of its member projects. If you would like to + support Mercurial directly, please consider making a donation to + the SFC on its behalf.

    + +

    If you would like to help free software developers to provide + their important public services without being impeded by legal + issues, please consider donating to the SFC's sister organisation, + the Software Freedom Law + Center.

    +
    +{% endblock %} diff -r 5981a0f7540a -r 019040fbf5f5 web/javascript/form-min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/javascript/form-min.js Tue Apr 21 00:36:40 2009 +0900 @@ -0,0 +1,1 @@ +(function($){$.fn.ajaxSubmit=function(_2){if(typeof _2=="function"){_2={success:_2};}_2=$.extend({url:this.attr("action")||window.location,type:this.attr("method")||"GET"},_2||{});var _3={};$.event.trigger("form.pre.serialize",[this,_2,_3]);if(_3.veto){return this;}var a=this.formToArray(_2.semantic);if(_2.data){for(var n in _2.data){a.push({name:n,value:_2.data[n]});}}if(_2.beforeSubmit&&_2.beforeSubmit(a,this,_2)===false){return this;}$.event.trigger("form.submit.validate",[a,this,_2,_3]);if(_3.veto){return this;}var q=$.param(a);if(_2.type.toUpperCase()=="GET"){_2.url+=(_2.url.indexOf("?")>=0?"&":"?")+q;_2.data=null;}else{_2.data=q;}var _7=this,callbacks=[];if(_2.resetForm){callbacks.push(function(){_7.resetForm();});}if(_2.clearForm){callbacks.push(function(){_7.clearForm();});}if(!_2.dataType&&_2.target){var _8=_2.success||function(){};callbacks.push(function(_9){if(this.evalScripts){$(_2.target).attr("innerHTML",_9).evalScripts().each(_8,arguments);}else{$(_2.target).html(_9).each(_8,arguments);}});}else{if(_2.success){callbacks.push(_2.success);}}_2.success=function(_a,_b){for(var i=0,max=callbacks.length;i");var io=$io[0];var op8=$.browser.opera&&window.opera.version()<9;if($.browser.msie||op8){io.src="javascript:false;document.write(\"\");";}$io.css({position:"absolute",top:"-1000px",left:"-1000px"});var xhr={responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){}};var g=_11.global;if(g&&!$.active++){$.event.trigger("ajaxStart");}if(g){$.event.trigger("ajaxSend",[xhr,_11]);}var _18=0;var _19=0;setTimeout(function(){$io.appendTo("body");io.attachEvent?io.attachEvent("onload",cb):io.addEventListener("load",cb,false);var _1a=_10.encoding?"encoding":"enctype";var t=_7.attr("target");_7.attr({target:id,method:"POST",action:_11.url});_10[_1a]="multipart/form-data";if(_11.timeout){setTimeout(function(){_19=true;cb();},_11.timeout);}_10.submit();_7.attr("target",t);},10);function cb(){if(_18++){return;}io.detachEvent?io.detachEvent("onload",cb):io.removeEventListener("load",cb,false);var ok=true;try{if(_19){throw "timeout";}var _1d,doc;doc=io.contentWindow?io.contentWindow.document:io.contentDocument?io.contentDocument:io.document;xhr.responseText=doc.body?doc.body.innerHTML:null;xhr.responseXML=doc.XMLDocument?doc.XMLDocument:doc;if(_11.dataType=="json"||_11.dataType=="script"){var ta=doc.getElementsByTagName("textarea")[0];_1d=ta?ta.value:xhr.responseText;if(_11.dataType=="json"){eval("data = "+_1d);}else{$.globalEval(_1d);}}else{if(_11.dataType=="xml"){_1d=xhr.responseXML;if(!_1d&&xhr.responseText!=null){_1d=toXml(xhr.responseText);}}else{_1d=xhr.responseText;}}}catch(e){ok=false;$.handleError(_11,xhr,"error",e);}if(ok){_11.success(_1d,"success");if(g){$.event.trigger("ajaxSuccess",[xhr,_11]);}}if(g){$.event.trigger("ajaxComplete",[xhr,_11]);}if(g&&!--$.active){$.event.trigger("ajaxStop");}if(_11.complete){_11.complete(xhr,ok?"success":"error");}setTimeout(function(){$io.remove();xhr.responseXML=null;},100);}function toXml(s,doc){if(window.ActiveXObject){doc=new ActiveXObject("Microsoft.XMLDOM");doc.async="false";doc.loadXML(s);}else{doc=(new DOMParser()).parseFromString(s,"text/xml");}return (doc&&doc.documentElement&&doc.documentElement.tagName!="parsererror")?doc:null;}}};$.fn.ajaxSubmit.counter=0;$.fn.ajaxForm=function(_21){return this.ajaxFormUnbind().submit(submitHandler).each(function(){this.formPluginId=$.fn.ajaxForm.counter++;$.fn.ajaxForm.optionHash[this.formPluginId]=_21;$(":submit,input:image",this).click(clickHandler);});};$.fn.ajaxForm.counter=1;$.fn.ajaxForm.optionHash={};function clickHandler(e){var _23=this.form;_23.clk=this;if(this.type=="image"){if(e.offsetX!=undefined){_23.clk_x=e.offsetX;_23.clk_y=e.offsetY;}else{if(typeof $.fn.offset=="function"){var _24=$(this).offset();_23.clk_x=e.pageX-_24.left;_23.clk_y=e.pageY-_24.top;}else{_23.clk_x=e.pageX-this.offsetLeft;_23.clk_y=e.pageY-this.offsetTop;}}}setTimeout(function(){_23.clk=_23.clk_x=_23.clk_y=null;},10);}function submitHandler(){var id=this.formPluginId;var _26=$.fn.ajaxForm.optionHash[id];$(this).ajaxSubmit(_26);return false;}$.fn.ajaxFormUnbind=function(){this.unbind("submit",submitHandler);return this.each(function(){$(":submit,input:image",this).unbind("click",clickHandler);});};$.fn.formToArray=function(_27){var a=[];if(this.length==0){return a;}var _29=this[0];var els=_27?_29.getElementsByTagName("*"):_29.elements;if(!els){return a;}for(var i=0,max=els.length;i= 0 ? '&' : '?') + q; + options.data = null; // data is null for 'get' + } + else + options.data = q; // data is the query string for 'post' + + var $form = this, callbacks = []; + if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); + if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); + + // perform a load on the target only if dataType is not provided + if (!options.dataType && options.target) { + var oldSuccess = options.success || function(){}; + callbacks.push(function(data) { + if (this.evalScripts) + $(options.target).attr("innerHTML", data).evalScripts().each(oldSuccess, arguments); + else // jQuery v1.1.4 + $(options.target).html(data).each(oldSuccess, arguments); + }); + } + else if (options.success) + callbacks.push(options.success); + + options.success = function(data, status) { + for (var i=0, max=callbacks.length; i < max; i++) + callbacks[i](data, status, $form); + }; + + // are there files to upload? + var files = $('input:file', this).fieldValue(); + var found = false; + for (var j=0; j < files.length; j++) + if (files[j]) + found = true; + + if (options.iframe || found) // options.iframe allows user to force iframe mode + fileUpload(); + else + $.ajax(options); + + // fire 'notify' event + $.event.trigger('form.submit.notify', [this, options]); + return this; + + + // private function for handling file uploads (hat tip to YAHOO!) + function fileUpload() { + var form = $form[0]; + var opts = $.extend({}, $.ajaxSettings, options); + + var id = 'jqFormIO' + $.fn.ajaxSubmit.counter++; + var $io = $('