# HG changeset patch # User Igor T«¡mara # Date 1230388245 18000 # Node ID a1b640641d376781e699d65ceda356916fa9f4a1 # Parent 622faa5f5bb8a9af9dc876eaf8218e0a50cc2813# Parent 97e929385442d9f39f6a5abc796f2d2156f35201 merging from serpentine diff -r 97e929385442 -r a1b640641d37 en/99defs.tex --- a/en/99defs.tex Sun Aug 31 12:14:23 2008 -0400 +++ b/en/99defs.tex Sat Dec 27 09:30:45 2008 -0500 @@ -136,6 +136,10 @@ % Reference entry for a command option with only long form. \newcommand{\loptref}[2]{\subsubsection{\hgopt{#1}{--#2} option}} +% command to generate a footnote to be used as a translator's note +\newcommand{\ndt}[1]{\footnote{\textbf{N. del T.} #1}} + + %%% Local Variables: %%% mode: latex %%% TeX-master: "00book" diff -r 97e929385442 -r a1b640641d37 en/examples/bisect --- a/en/examples/bisect Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect Sat Dec 27 09:30:45 2008 -0500 @@ -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,7 +37,11 @@ #$ 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 diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.commits.out --- a/en/examples/bisect.commits.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.commits.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,16 +1,3 @@ - - - - - - - - - - - - - @@ -21,25 +8,3 @@ - - - - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.help.out --- a/en/examples/bisect.help.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.help.out Sat Dec 27 09:30:45 2008 -0500 @@ -19,3 +19,5 @@ + + diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.init.out --- a/en/examples/bisect.init.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.init.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,3 +1,2 @@ - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.bad-init.out --- a/en/examples/bisect.search.bad-init.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.bad-init.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,22 +1,1 @@ - - - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.good-init.out --- a/en/examples/bisect.search.good-init.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.good-init.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,22 +1,3 @@ - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.init.out --- a/en/examples/bisect.search.init.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.init.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,22 +1,5 @@ - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.reset.out --- a/en/examples/bisect.search.reset.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.reset.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,22 +1,1 @@ - - - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.rest.out --- a/en/examples/bisect.search.rest.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.rest.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,22 +1,3 @@ - - - - - - - - - - - - - - - - - - - @@ -33,37 +14,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.step1.out --- a/en/examples/bisect.search.step1.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.step1.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,7 +1,3 @@ - - - - @@ -13,18 +9,3 @@ - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/bisect.search.step2.out --- a/en/examples/bisect.search.step2.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/bisect.search.step2.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,23 +1,4 @@ - - - - - - - - - - - - - - - - - - - diff -r 97e929385442 -r a1b640641d37 en/examples/branch-repo.bugfix.out --- a/en/examples/branch-repo.bugfix.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/branch-repo.bugfix.out Sat Dec 27 09:30:45 2008 -0500 @@ -5,7 +5,7 @@ $ \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 +pushing to searching for changes adding changesets adding manifests diff -r 97e929385442 -r a1b640641d37 en/examples/branching.stable.out --- a/en/examples/branching.stable.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/branching.stable.out Sat Dec 27 09:30:45 2008 -0500 @@ -5,7 +5,7 @@ $ \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 +pushing to searching for changes adding changesets adding manifests diff -r 97e929385442 -r a1b640641d37 en/examples/daily.copy.merge.out --- a/en/examples/daily.copy.merge.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/daily.copy.merge.out Sat Dec 27 09:30:45 2008 -0500 @@ -7,7 +7,7 @@ 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 to new-file +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} diff -r 97e929385442 -r a1b640641d37 en/examples/tour-merge-conflict.commit.out --- a/en/examples/tour-merge-conflict.commit.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/tour-merge-conflict.commit.out Sat Dec 27 09:30:45 2008 -0500 @@ -4,6 +4,29 @@ > \textbf{Nigerian dictator Sani Abacha.} > \textbf{EOF} $ \textbf{hg resolve -m letter.txt} +hg: unknown command 'resolve' +Mercurial Distributed SCM + +basic commands: + + add add the specified files on the next commit + annotate show changeset information per file line + clone make a copy of an existing repository + commit commit the specified files or all outstanding changes + diff diff repository (or selected files) + export dump the header and diffs for one or more changesets + init create a new repository in the given directory + log show revision history of entire repository or files + merge merge working directory with another revision + parents show the parents of the working dir or revision + pull pull changes from the specified source + push push changes to the specified destination + remove remove the specified files on the next commit + serve export the repository via HTTP + status show changed files in the working directory + update update working directory + +use "hg help" for the full list of commands or "hg -v" for details $ \textbf{hg commit -m 'Send me your money'} $ \textbf{hg tip} changeset: diff -r 97e929385442 -r a1b640641d37 en/examples/tour-merge-conflict.merge.out --- a/en/examples/tour-merge-conflict.merge.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/tour-merge-conflict.merge.out Sat Dec 27 09:30:45 2008 -0500 @@ -4,7 +4,9 @@ merge: warning: conflicts during merge merging letter.txt failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved -use 'hg resolve' to retry unresolved file merges +There are unresolved merges, you can redo the full merge using: + hg update -C 1 + hg merge 2 $ \textbf{cat letter.txt} Greetings! diff -r 97e929385442 -r a1b640641d37 en/examples/tour.help.out --- a/en/examples/tour.help.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/tour.help.out Sat Dec 27 09:30:45 2008 -0500 @@ -3,7 +3,7 @@ create a new repository in the given directory - Initialize a new repository in the given directory. If the given + 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. diff -r 97e929385442 -r a1b640641d37 en/examples/tour.ls.out --- a/en/examples/tour.ls.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/tour.ls.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,5 +1,5 @@ $ \textbf{ls -l} -total 4 + $ \textbf{ls hello} Makefile hello.c diff -r 97e929385442 -r a1b640641d37 en/examples/tour.version.out --- a/en/examples/tour.version.out Sun Aug 31 12:14:23 2008 -0400 +++ b/en/examples/tour.version.out Sat Dec 27 09:30:45 2008 -0500 @@ -1,5 +1,5 @@ $ \textbf{hg version} -Mercurial Distributed SCM (version ) +Mercurial Distributed SCM (version 1.0) Copyright (C) 2005-2008 Matt Mackall and others This is free software; see the source for copying conditions. There is NO diff -r 97e929385442 -r a1b640641d37 en/tour-merge.tex diff -r 97e929385442 -r a1b640641d37 es/00book.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/00book.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,79 @@ +% 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 +\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{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://hg.serpentine.com/mercurial/book/}{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}{Bibliography} +\bibliographystyle{alpha} +\bibliography{99book} + +\addcontentsline{toc}{chapter}{Index} +\printindex + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r 97e929385442 -r a1b640641d37 es/99book.bib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99book.bib Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/99book.bib \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/99defs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99defs.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,146 @@ +% Bug ID. +\newcommand{\bug}[1]{\index{Mercurial bug + database!\href{http://www.selenic.com/mercurial/bts/issue#1}{bug + ~#1}}\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial + bug no.~#1}} + +% File name in the user's home directory. +\newcommand{\tildefile}[1]{\texttt{\~{}/#1}} + +% File name. +\newcommand{\filename}[1]{\texttt{#1}} + +% Directory name. +\newcommand{\dirname}[1]{\texttt{#1}} + +% File name, with index entry. +% The ``s'' prefix comes from ``special''. +\newcommand{\sfilename}[1]{\index{\texttt{#1} file}\texttt{#1}} + +% Directory name, with index entry. +\newcommand{\sdirname}[1]{\index{\texttt{#1} directory}\texttt{#1}} + +% Mercurial extension. +\newcommand{\hgext}[1]{\index{\texttt{#1} extension}\texttt{#1}} + +% Command provided by a Mercurial extension. +\newcommand{\hgxcmd}[2]{\index{\texttt{#2} command (\texttt{#1} + extension)}\index{\texttt{#1} extension!\texttt{#2} command}``\texttt{hg #2}''} + +% Mercurial command. +\newcommand{\hgcmd}[1]{\index{\texttt{#1} command}``\texttt{hg #1}''} + +% Mercurial command, with arguments. +\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''} + +\newcommand{\tplkword}[1]{\index{\texttt{#1} template keyword}\index{template keywords!\texttt{#1}}\texttt{#1}} + +\newcommand{\tplkwfilt}[2]{\index{\texttt{#1} template keyword!\texttt{#2} + filter}\index{template filters!\texttt{#2}}\index{\texttt{#2} + template filter}\texttt{#2}} + +\newcommand{\tplfilter}[1]{\index{template + filters!\texttt{#1}}\index{\texttt{#1} template + filter}\texttt{#1}} + +% Shell/system command. +\newcommand{\command}[1]{\index{\texttt{#1} system command}\texttt{#1}} + +% Shell/system command, with arguments. +\newcommand{\cmdargs}[2]{\index{\texttt{#1} system command}``\texttt{#1 #2}''} + +% Mercurial command option. +\newcommand{\hgopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}} + +% Mercurial command option, provided by an extension command. +\newcommand{\hgxopt}[3]{\index{\texttt{#2} command (\texttt{#1} extension)!\texttt{#3} option}\index{\texttt{#1} extension!\texttt{#2} command!\texttt{#3} option}\texttt{#3}} + +% Mercurial global option. +\newcommand{\hggopt}[1]{\index{global options!\texttt{#1} option}\texttt{#1}} + +% Shell/system command option. +\newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}} + +% Command option. +\newcommand{\option}[1]{\texttt{#1}} + +% Software package. +\newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}} + +% Section name from a hgrc file. +\newcommand{\rcsection}[1]{\index{\texttt{hgrc} file!\texttt{#1} section}\texttt{[#1]}} + +% Named item in a hgrc file section. +\newcommand{\rcitem}[2]{\index{\texttt{hgrc} file!\texttt{#1} + section!\texttt{#2} entry}\texttt{#2}} + +% hgrc file. +\newcommand{\hgrc}{\index{configuration file!\texttt{hgrc} + (Linux/Unix)}\index{\texttt{hgrc} configuration file}\texttt{hgrc}} + +% Mercurial.ini file. +\newcommand{\hgini}{\index{configuration file!\texttt{Mercurial.ini} + (Windows)}\index{\texttt{Mercurial.ini} configuration file}\texttt{Mercurial.ini}} + +% Hook name. +\newcommand{\hook}[1]{\index{\texttt{#1} hook}\index{hooks!\texttt{#1}}\texttt{#1}} + +% Environment variable. +\newcommand{\envar}[1]{\index{\texttt{#1} environment + variable}\index{environment variables!\texttt{#1}}\texttt{#1}} + +% Python module. +\newcommand{\pymod}[1]{\index{\texttt{#1} module}\texttt{#1}} + +% Python class in a module. +\newcommand{\pymodclass}[2]{\index{\texttt{#1} module!\texttt{#2} + class}\texttt{#1.#2}} + +% Python function in a module. +\newcommand{\pymodfunc}[2]{\index{\texttt{#1} module!\texttt{#2} + function}\texttt{#1.#2}} + +% Note: blah blah. +\newsavebox{\notebox} +\newenvironment{note}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Nota:}\space}% + {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}} +\newenvironment{caution}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Precaución:}\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} command}} + +% Reference entry for a command option with long and short forms. +\newcommand{\optref}[3]{\subsubsection{\hgopt{#1}{--#3}, also \hgopt{#1}{-#2}}} + +% Reference entry for a command option with only long form. +\newcommand{\loptref}[2]{\subsubsection{\hgopt{#1}{--#2} option}} + +% command to generate a footnote to be used as a translator's note +\newcommand{\ndt}[1]{\footnote{\textbf{N. del T.} #1}} + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 97e929385442 -r a1b640641d37 es/Leame.1st --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Leame.1st Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,467 @@ += 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 || +|| branch.tex || Igor Támara || 100% || 16/10/2008 || 19/10/2008 || +|| preface.tex || Javier Rojas || 100% || 18/10/2008 || 19/10/2008 || +|| daily.tex || Igor Támara || 100% || 19/10/2008 || 26/10/2008 || +|| tour-basic.tex || Javier Rojas || 100% || 19/10/2008 || 27/10/2008 || +|| undo.tex || Igor Támara || 100% || 26/10/2008 || 07/11/2008 || +|| tour-merge.tex || Javier Rojas || 100% || 28/10/2008 || 03/11/2008 || +|| concepts.tex || Javier Rojas || 100% || 03/11/2008 || 23/11/2008 || +|| intro.tex || Igor Támara || 100% || 08/11/2008 || 09/11/2008 || +|| collab.tex || Igor Támara || 100% || 10/11/2008 || 06/12/2008 || +|| filenames.tex || Javier Rojas || 72% || 27/11/2008 || || +|| hook.tex || Javier Rojas || 26% || 01/12/2008 || || +|| mq.tex || Igor Támara || 100% || 06/12/2008 || 13/12/2008 || +|| hgext.tex || Igor Támara || 100% || 13/12/2008 || 16/12/2008 || +|| template.tex || Igor Támara || || || || +|| mq-collab.tex || Javier Rojas || || || || +|| mq-ref.tex || Javier Rojas || || || || +|| cmdref.tex || || || || || +|| license.tex || Igor Támara || 100% || 16/12/2008 || 16/12/2008 || +|| srcinstall.tex || || || || || + +== Archivos en proceso de revisión == +||'''archivo''' || '''revisor''' ||'''Estado'''||'''Inicio'''|| '''Fin''' || +|| 00book.tex || Javier Rojas || || || || +|| branch.tex || Javier Rojas || || || || +|| preface.tex || || || || || +|| daily.tex || Javier Rojas || || || || +|| tour-basic.tex || || || || || +|| undo.tex || Javier Rojas || || || || +|| tour-merge.tex || || || || || +|| concepts.tex || || || || || +|| intro.tex || || || || || +|| collab.tex || Javier Rojas || || || || +|| mq.tex || || || || || + +== Archivos terminados == + += 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 + Changelog: Bitácora de Cambios + Changeset: Conjunto de Cambios + Command: Orden + Commit: Consignar + Core: alma + Directory: Directorio + File: fichero + Filelog: fichero de registro + Fold: Integrar + Fork: Bifurcación + 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 + 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 + Sprint: sprint + Tarball: paquete de cambios + 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? + +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 layout -> 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". + += 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 97e929385442 -r a1b640641d37 es/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Makefile Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,220 @@ +# This makefile requires GNU make. + +sources := \ + 00book.tex \ + 99book.bib \ + 99defs.tex \ + build_id.tex \ + branch.tex \ + cmdref.tex \ + collab.tex \ + concepts.tex \ + daily.tex \ + filenames.tex \ + hg_id.tex \ + hgext.tex \ + hook.tex \ + intro.tex \ + mq.tex \ + mq-collab.tex \ + mq-ref.tex \ + preface.tex \ + srcinstall.tex \ + template.tex \ + tour-basic.tex \ + tour-merge.tex \ + undo.tex + +image-sources := \ + feature-branches.dot \ + filelog.svg \ + kdiff3.png \ + metadata.svg \ + mq-stack.svg \ + note.png \ + revlog.svg \ + snapshot.svg \ + tour-history.svg \ + tour-merge-conflict.svg \ + tour-merge-merge.svg \ + tour-merge-pull.svg \ + tour-merge-sep-repos.svg \ + undo-manual.dot \ + undo-manual-merge.dot \ + undo-non-tip.dot \ + undo-simple.dot \ + wdir.svg \ + wdir-after-commit.svg \ + wdir-branch.svg \ + wdir-merge.svg \ + wdir-pre-branch.svg + +image-dot := $(filter %.dot,$(image-sources)) +image-svg := $(filter %.svg,$(image-sources)) +image-png := $(filter %.png,$(image-sources)) + +image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) +image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) + +example-sources := \ + backout \ + bisect \ + branching \ + branch-named \ + branch-repo \ + cmdref \ + daily.copy \ + daily.files \ + daily.rename \ + daily.revert \ + extdiff \ + filenames \ + hook.msglen \ + hook.simple \ + hook.ws \ + issue29 \ + mq.guards \ + mq.qinit-help \ + mq.dodiff \ + mq.id \ + mq.tarball \ + mq.tools \ + mq.tutorial \ + rename.divergent \ + rollback \ + tag \ + template.simple \ + template.svnstyle \ + tour \ + tour-merge-conflict + +example-prereqs := \ + /usr/bin/merge + +dist-sources := \ + ../html/hgicon.png \ + ../html/index.html.var \ + ../html/index.en.html + +latex-options = \ + -interaction batchmode \ + -output-directory $(dir $(1)) \ + -jobname $(basename $(notdir $(1))) + +hg = $(shell which hg) + +hg-id = $(shell hg parents --template '{node|short}, dated {date|isodate},\n') + +hg-version = $(shell hg version -q | \ + sed 's,.*(version \(unknown\|[a-f0-9+]*\)),\1,') + +all: pdf html + +pdf: pdf/hgbook.pdf + +define pdf + mkdir -p $(dir $@) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + cp 99book.bib $(dir $@) + cd $(dir $@) && bibtex $(basename $(notdir $@)) + cd $(dir $@) && makeindex $(basename $(notdir $@)) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + if grep 'Reference.*undefined' $(@:.pdf=.log); then exit 1; fi +endef + +pdf/hgbook.pdf: $(sources) 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 $(dist-sources) dist + +rsync: install + rsync -avz --delete dist sp.red-bean.com:public_html/hgbook diff -r 97e929385442 -r a1b640641d37 es/bookhtml.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/bookhtml.cfg Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/bookhtml.cfg \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/branch.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/branch.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,408 @@ +\chapter{Administración de Versiones y desarrollo ramificado} +\label{chap:branch} + +Mercurial ofrece varios mecanismos que le permitirán 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''. Estas usualmente son idénticas a las +versiones mayores en las cuales están basadas, pero con arreglo de +algunos fallos. + +En este capítulo, comenzaremos hablando de cómo mantener registro de +las etapas del proyecto como las liberaciones de una +versión. Continuaremos hablando del flujo de trabajo entre las +diferentes fases de un proyecto, y como puede ayudar Mercurial a +independizar y administrar tal trabajo. + +\section{Dar un nombre persistente a una revisión} + +Cuando se decide a otorgar a una revisión el nombre particular de una +``versión'', es buena idea grabar la identidad para tal revisión. +Lo cual permitirá reproducir tal versión en una fecha posterior, o el +propósito que se considere 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} + +Un tag no es más que un ``nombre simbólico'' para una revisión. Los +tags existen únicamente para su conveniencia, dotándolo de una forma +permanente y sencilla para referirse a una revisión; Mercurial no +interpreta de ninguna manera los nombres de los tags que usted use. +Mercurial tampoco impone restricción alguna al nombre de un tag, más +allá de lo necesario para asegurar que un tag puede parsearse sin +ambigüedades. El nombre de un tag no puede tener ninguno de los +caracteres siguientes: +\begin{itemize} +\item Dos puntos (ASCII 58, ``\texttt{:}'') +\item Retroceso (return) (ASCII 13, ``\Verb+\r+'') +\item Nueva línea (ASCII 10, ``\Verb+\n+'') +\end{itemize} + +Puede usar la orden \hgcmd{tags} para observar los tags presentes en +su repositorio. Al desplegarse, cada revisión marcada se identifica +primero con su nombre, después 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 la lista de \hgcmd{tags}. El tag +\texttt{tip} es un tag ``flotante'' especial, que identifica siempre +la revisión más nueva en el repositorio. + +Al desplegar la orden \hgcmd{tags}, los tags se listan en orden +inverso, por número de revisión. Lo que significa usualmente que los +tags más recientes se listan antes que los más antiguos. También +significa que el tag \texttt{tip} siempre aparecerá como primer tag +listado al desplegar la orden \hgcmd{tags}. + +Cuando ejecuta \hgcmd{log}, se desplegará la revisión que tenga los +tags asociados a ella, se imprimirán tales tags. +\interaction{tag.log} + +Siempre que requiera indicar un ~ID de revisión a una Orden de +Mercurial, aceptará un nombre de tag en su lugar. Internamente, +Mercurial traducirá su nombre de tag en el ~ID de revisión +correspondiente, y lo usará. +\interaction{tag.log.v1.0} + +No hay límites en la cantidad de tags por reposirorio, o la cantidad +de tags que una misma revisión pueda tener. Siendo prácticos, no es +muy buena idea tener ``demasiados'' (la cantidad variará de un +proyecto a otro), debido a que la intención es ayudarle a encontrar +revisiones. Si tiene demasiados tags, la facilidad para identificar +revisiones disminuirá rápidamente. + +Por ejemplo, si su proyecto tiene etapas(milestones) frecuentes en pocos +días, es perfectamente razonable asignarle un tag 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 un tag para cada generación +exitosa. Más bien, podría usar tags para generaciones fallidas (en +caso de que estas sean raras!), o simplemente evitar los tags para +llevar cuenta de la posibilidad de generación de binarios. + + +Si desea eliminar un tag que no desea, use +\hgcmdargs{tag}{--remove}. +\interaction{tag.remove} +También puede modificar un tag en cualquier momento para que +identifique una revisión distinta, basta con aplicar una nueva orden +\hgcmd{tag}. Deberá usar la opción \hgopt{tag}{-f} para indicarle a +Mercurial que desea actualizar el tag \emph{en serio}. +\interaction{tag.replace} +De todas maneras habrá un registro permanente de la antigua identidad +del tag, pero Mercurial no la usará. Por lo tanto no hay castigo al +marcar con un tag una revisión incorrecta; lo único que debe hacer es +mover el tag hacia la revisión correcta tan pronto como localice el +error. + +Mercurial almacena los tags en un archivo controlado por revisiones en +su repositorio. Si ha creado tags, los encontrará en un archivo +llamado \sfilename{.hgtags}. Cuando invoca la orden \hgcmd{tag}, +Mercurial modifica este archivo, y automáticamente hace commit del +cambio al mismo. Esto significa que cada vez que ejecuta \hgcmd{tag}, +verá el conjunto de cambios correspondiente en la salida de \hgcmd{log}. +\interaction{tag.tip} + +\subsection{Manejo de conflictos entre tags durante una fusión} + +Es usual no tener que preocuparse por el archivo \sfilename{.hgtags}, +pero aveces hace su aparición durante una fusión. El formato del +archivo 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 un tag. + +Si está resolviendo un conflicto en el archivo \sfilename{.hgtags} +durante una fusión, hay un detalle para tener en cuenta al modificar +el archivo \sfilename{.hgtags}: +cuando Mercurial parsea los tags en el repositorio \emph{nunca} +lee la copia de trabajo del archivo \sfilename{.hgtags}. En cambio, +lee la versión \emph{consignada más reciente} del archivo. + +Una consecuencia desafortunada de este diseño es que usted no puede +verificar que su archivo \sfilename{.hgtags} fusionado es correcto hasta +\emph{después} de haber consignado(hecho commit). 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 archivo \sfilename{.hgtags}, +reportará el lugar del error, que podrá arreglar y después +consignar. Posteriormente ejecute de nuevo la orden \hgcmd{tags} para +asegurar que su arreglo fue correctamente aplicado. + +\subsection{Tags 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á historia posterior a la revisión que usted haya +especificado. Esta forma de interactuar puede sorprender a los +desprevenidos. + +Recuerde que un tag se almacena como una revisión al archivo +\sfilename{.hgtags}, consecuente con esto, cuando crea un tag, el +conjunto de cambios en el cual este se almacena necesariamente se +refiere a un conjunto de cambios anterior. Cuando ejecuta +\hgcmdargs{clone}{-r foo} para clonar un repositorio hasta el tag +\texttt{foo}, el nuevo clon \emph{no contendrá la historia que creo +el tag} que usó para clonar el repositorio. El resultado es que tendrá +exactamente el subconjunto correcto de la historia del proyecto en el +nuevo repositorio, pero, \emph{no} el tag que podría haber esperado. + +\subsection{Cuando los tags permanentes son demasiado} + +Dado que los tags de Mercurial están controlados por revisiones y se +llevan en la historia del proyecto, todas las personas involucradas +verán los tags 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 bug sutil, posiblemente desearía colocar un +tag recordándole algo como ``Ana vió los síntomas con esta revisión''. + +Para estos casos, lo que posiblemente desearía serían tags +\emph{locales}. Puede crear un tag local con la opción \hgopt{tag}{-l} +de la orden \hgcmd{tag}. Esto guardará el tag en un archivo llamado +\sfilename{.hg/localtags}. A diferencia de \sfilename{.hgtags}, +\sfilename{.hg/localtags} no está controlado por revisiones. +Cualquier tag que usted cree usando \hgopt{tag}{-l} se mantendrá +localmente en el 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''; Un nueva +versión con un rreglo 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 es como ``ramas''. Aunque hemos visto que +en variadas ocasiones Mercurial trata a \emph{toda la historia} 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 al 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 un tag en 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 tag. +\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 publicarían(con push). +\interaction{branch-repo.bugfix} +Mientras tanto, el desarrollo para la siguiente versión mayor puede +continuar asilada 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 esté también 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 obetivo en su clon local. +\interaction{branch-repo.pull} +A continuación deberá mezclar las cabezas de las dos ramas, y publicar +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 +facilidad; 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(para los +nuevos a Mercurial) para trabajar con los archivos dentro de su +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 mencionador 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 comenzar a trabajar con ramas +nombradas. Esta orden mostrará las ramas presentes en su repositorio, +indicándole en qué conjunto de cambios está cada una. +\interaction{branch-named.branches} +Dado que todavía no ha creado ramas nombradas, la única que verá sería +\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 actual de cambios. +\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} commands report? +\interaction{branch-named.status} +Nada cambia en el directorio actual, y no se ha añadido nada a la +historia. 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 alamacena 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én 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 al tip de esta rama, sin +importar cuál sea el tip ``a lo largo del repositorio'' . Para +actualiar 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, 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 nuestra historia 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 al tipo 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 and 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 com elige Mercurial el nombre de la rama cuando hace +fusión. Después de una fusión Mercurial mantendrá el nombre de la +rama del primer padre cuando consigne el resultado de una 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 +desde la rama \texttt{foo}, el resultado permanecerá en la rama +\texttt{bar}. +\interaction{branch-named.merge} + +En un ejemplo más concreo, 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 la +historia 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 +accidentalmente publique 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 97e929385442 -r a1b640641d37 es/cmdref.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.py Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/cmdref.py \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/cmdref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,176 @@ +\chapter{Command reference} +\label{cmdref} + +\cmdref{add}{add files at the next commit} +\optref{add}{I}{include} +\optref{add}{X}{exclude} +\optref{add}{n}{dry-run} + +\cmdref{diff}{print changes in history or working directory} + +Show differences between revisions for the specified files or +directories, using the unified diff format. For a description of the +unified diff format, see section~\ref{sec:mq:patch}. + +By default, this command does not print diffs for files that Mercurial +considers to contain binary data. To control this behaviour, see the +\hgopt{diff}{-a} and \hgopt{diff}{--git} options. + +\subsection{Options} + +\loptref{diff}{nodates} + +Omit date and time information when printing diff headers. + +\optref{diff}{B}{ignore-blank-lines} + +Do not print changes that only insert or delete blank lines. A line +that contains only whitespace is not considered blank. + +\optref{diff}{I}{include} + +Include files and directories whose names match the given patterns. + +\optref{diff}{X}{exclude} + +Exclude files and directories whose names match the given patterns. + +\optref{diff}{a}{text} + +If this option is not specified, \hgcmd{diff} will refuse to print +diffs for files that it detects as binary. Specifying \hgopt{diff}{-a} +forces \hgcmd{diff} to treat all files as text, and generate diffs for +all of them. + +This option is useful for files that are ``mostly text'' but have a +few embedded NUL characters. If you use it on files that contain a +lot of binary data, its output will be incomprehensible. + +\optref{diff}{b}{ignore-space-change} + +Do not print a line if the only change to that line is in the amount +of white space it contains. + +\optref{diff}{g}{git} + +Print \command{git}-compatible diffs. XXX reference a format +description. + +\optref{diff}{p}{show-function} + +Display the name of the enclosing function in a hunk header, using a +simple heuristic. This functionality is enabled by default, so the +\hgopt{diff}{-p} option has no effect unless you change the value of +the \rcitem{diff}{showfunc} config item, as in the following example. +\interaction{cmdref.diff-p} + +\optref{diff}{r}{rev} + +Specify one or more revisions to compare. The \hgcmd{diff} command +accepts up to two \hgopt{diff}{-r} options to specify the revisions to +compare. + +\begin{enumerate} +\setcounter{enumi}{0} +\item Display the differences between the parent revision of the + working directory and the working directory. +\item Display the differences between the specified changeset and the + working directory. +\item Display the differences between the two specified changesets. +\end{enumerate} + +You can specify two revisions using either two \hgopt{diff}{-r} +options or revision range notation. For example, the two revision +specifications below are equivalent. +\begin{codesample2} + hg diff -r 10 -r 20 + hg diff -r10:20 +\end{codesample2} + +When you provide two revisions, Mercurial treats the order of those +revisions as significant. Thus, \hgcmdargs{diff}{-r10:20} will +produce a diff that will transform files from their contents as of +revision~10 to their contents as of revision~20, while +\hgcmdargs{diff}{-r20:10} means the opposite: the diff that will +transform files from their revision~20 contents to their revision~10 +contents. You cannot reverse the ordering in this way if you are +diffing against the working directory. + +\optref{diff}{w}{ignore-all-space} + +\cmdref{version}{print version and copyright information} + +This command displays the version of Mercurial you are running, and +its copyright license. There are four kinds of version string that +you may see. +\begin{itemize} +\item The string ``\texttt{unknown}''. This version of Mercurial was + not built in a Mercurial repository, and cannot determine its own + version. +\item A short numeric string, such as ``\texttt{1.1}''. This is a + build of a revision of Mercurial that was identified by a specific + tag in the repository where it was built. (This doesn't necessarily + mean that you're running an official release; someone else could + have added that tag to any revision in the repository where they + built Mercurial.) +\item A hexadecimal string, such as ``\texttt{875489e31abe}''. This + is a build of the given revision of Mercurial. +\item A hexadecimal string followed by a date, such as + ``\texttt{875489e31abe+20070205}''. This is a build of the given + revision of Mercurial, where the build repository contained some + local changes that had not been committed. +\end{itemize} + +\subsection{Tips and tricks} + +\subsubsection{Why do the results of \hgcmd{diff} and \hgcmd{status} + differ?} +\label{cmdref:diff-vs-status} + +When you run the \hgcmd{status} command, you'll see a list of files +that Mercurial will record changes for the next time you perform a +commit. If you run the \hgcmd{diff} command, you may notice that it +prints diffs for only a \emph{subset} of the files that \hgcmd{status} +listed. There are two possible reasons for this. + +The first is that \hgcmd{status} prints some kinds of modifications +that \hgcmd{diff} doesn't normally display. The \hgcmd{diff} command +normally outputs unified diffs, which don't have the ability to +represent some changes that Mercurial can track. Most notably, +traditional diffs can't represent a change in whether or not a file is +executable, but Mercurial records this information. + +If you use the \hgopt{diff}{--git} option to \hgcmd{diff}, it will +display \command{git}-compatible diffs that \emph{can} display this +extra information. + +The second possible reason that \hgcmd{diff} might be printing diffs +for a subset of the files displayed by \hgcmd{status} is that if you +invoke it without any arguments, \hgcmd{diff} prints diffs against the +first parent of the working directory. If you have run \hgcmd{merge} +to merge two changesets, but you haven't yet committed the results of +the merge, your working directory has two parents (use \hgcmd{parents} +to see them). While \hgcmd{status} prints modifications relative to +\emph{both} parents after an uncommitted merge, \hgcmd{diff} still +operates relative only to the first parent. You can get it to print +diffs relative to the second parent by specifying that parent with the +\hgopt{diff}{-r} option. There is no way to print diffs relative to +both parents. + +\subsubsection{Generating safe binary diffs} + +If you use the \hgopt{diff}{-a} option to force Mercurial to print +diffs of files that are either ``mostly text'' or contain lots of +binary data, those diffs cannot subsequently be applied by either +Mercurial's \hgcmd{import} command or the system's \command{patch} +command. + +If you want to generate a diff of a binary file that is safe to use as +input for \hgcmd{import}, use the \hgcmd{diff}{--git} option when you +generate the patch. The system \command{patch} command cannot handle +binary patches at all. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 97e929385442 -r a1b640641d37 es/collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/collab.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 la historia de un repositorio, examinar cada +cambio(comentarios y diferencias), y ver los contenidos de cada +directorio y fichero. + +Adicionalmente la interfaz provee feeds de RSS de los cambios de los +repositorios. Que le permite ``subscribirse''a un repositorio usando +su herramienta de lectura de feeds 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 tunneling +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 la historia del repositorio. + +\subsection{Trabajo con muchas ramas} + +Los proyectos de cierta talla tienden naturlamente 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 contendtrá 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 cabmios, 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 matiene 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ícamente 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á intimamente 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 la historia 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á familizarizado 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 omitirl 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 chasa 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 teclera 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 autentitcación es un daemonio 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 alguntos 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 ícono 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 occuren 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 obitene un error de ``conexión rehusada'', es posible que no + haya un daemonio 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 daemonio 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 daemonio de ssh está escuchando(usualmente el~22). +No trate de buscar otras posibilidades exóticas o configuraciones +erradas haste 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 archivo, el daemonio ssh no confiará o lo leerá. +\end{itemize} + +En un mundo ideal, debería poder ejecutar la siguiente orden +exitósamente, 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 imprimie +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 script se ejecuta en un shell +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 shell 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 nisiquiera + 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 discresió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{not} 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 continúació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 script. En cuyo +caso, es posible que los permisos que tiene son \emph{demasiado + permisivos}. El subsistema \texttt{suexec} de Apache no ejecutará un +script que tenga permisos de escritura para el group 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 script 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 script 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 reemplaze 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 de la historia 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 script 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 mostar 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 archivo 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 archivo 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 archivos. + +Si lo desea, puede usar los dos mecanismos \texttt{collections} y +\texttt{paths} simultáneamente en un sólo archivo 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 ususarios 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 archivos + 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 taza 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ó, 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 archivos 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 97e929385442 -r a1b640641d37 es/concepts.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/concepts.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 de el +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 archivos 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 97e929385442 -r a1b640641d37 es/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/daily.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,397 @@ +\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 +explícitamente se lo indique. La orden \hgcmd{status} le mostrará +cuáles ficheros son desconocidos para Mercurial; 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 hacerles algo---de forma +predeterminada. Si tiene un repositorio que contiene miles de +ficheros, inusualmente 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. +A 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 explicíto 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 tratará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 efectua un paso extra al 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 fichero. 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 son inusualmente útiles, hay soluciones +alternativas no intrusivas que 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 ``escondido'' dentro de ese directorio. En sistemas tipo +Unix, cualquier fichero cuyo nombre comience con un punto +(``\texttt{.}'') se trata como escondido por la mayoría de +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 escondido} + \label{ex:daily:hidden} +\end{figure} + +Otra forma de abordar la necesidad de un fichero vacío es crear +simplemente uno en sus guiones de construcción antes de ser necesarios. + +\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 historia} + +Es preciso tener en cuenta que al eliminar un fichero se tiene +dos efectos solamente. +\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 la +\emph{historia} del mismo. + +Si actualiza su directorio de trabajo a un conjunto de cambios en el +cual esl fichero que eliminó aún era tenido en cuenta, 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 cuanta de la ausencia del fichero +automáticamente después de la ejecución de \hgcmd{commit}, y dejaba 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} su puede usar con la opción \hgopt{commit}{-A} +que aplica el 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 nueva copia 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. Trata de forma especial los ficheros copiados 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 trabajar algo en paralelo, de forma que tengamos algo para +fusionar. Aquí clonamos el repositorio. +\interaction{daily.copy.clone} +De vuelta en el repositorio, 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 +como un fichero que se ha añadido normalmente. +\interaction{daily.copy.status} +Si usamos la opción \hgopt{status}{-C} de la orden \hgcmd{status}, +imprimirá otra línea: Ela fichero of output: this is the file that our newly-added +file was copied \emph{from}. +\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 oficinal 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, use +la orden usual de sus sistema para copiar ficheros (En sistemas tipo +Unix, es \command{cp}), 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 del 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} actua 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 este. +\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 como 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 fichero. 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ñadido y el fichero inicial de la copia, como eliminado. +\interaction{daily.rename.status} +De la misma forma como 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 renombrar 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 cambios respectivos, mi +modificación al fichero bajo su nombre original se propagará en el +fichero con el nuevo nombre. (Es lo que se esperaría como ``lo hace,'' +pero, no todos los sistemas de control de revisiones lo logran.) + +El hecho de que los cambios sigan la copia es una característica que +puede subestimar diciendo ``si, puede ser útil,'' debería ser claro +que el seguimiento de cambios de un renombramiento es importante +definitivamente. Sin esto, sería muy sencillo que los cambios se +volvieran huérfanos 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} +Ana renombra el fichero a \filename{bar}. +\interaction{rename.divergent.rename.anne} +Mientras que Roberto 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 +renombrado el fichero. + +¿ué 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. +\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 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 guiarlo a una solución adecuada. + +\subsection{Otros casos límites 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 tando 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 ser +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 97e929385442 -r a1b640641d37 es/examples/backout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/backout Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/bisect --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/bisect Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/branch-named --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-named Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/branch-repo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-repo Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/branching --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branching Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/cmdref --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/cmdref Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/daily.copy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.copy Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.files Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/daily.rename --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.rename Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/daily.revert --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.revert Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/data/check_whitespace.py Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/data/netplug-1.2.5.tar.bz2 Binary file es/examples/data/netplug-1.2.5.tar.bz2 has changed diff -r 97e929385442 -r a1b640641d37 es/examples/data/netplug-1.2.8.tar.bz2 Binary file es/examples/data/netplug-1.2.8.tar.bz2 has changed diff -r 97e929385442 -r a1b640641d37 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 Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/extdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/extdiff Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/filenames --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/filenames Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/hook.msglen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.msglen Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/hook.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.simple Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.ws Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/issue29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/issue29 Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.dodiff Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.guards --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.guards Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.id --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.id Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.qinit-help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.qinit-help Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,7 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: help +hg help qinit diff -r 97e929385442 -r a1b640641d37 es/examples/mq.tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tarball Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.tools --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tools Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/mq.tutorial --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tutorial Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/rename.divergent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rename.divergent Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/rollback --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rollback Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/run-example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/run-example Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/svn-long.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-long.txt Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/svn-short.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-short.txt Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/svn.style --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.style Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,2 @@ +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template diff -r 97e929385442 -r a1b640641d37 es/examples/svn.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.template Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,5 @@ +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ diff -r 97e929385442 -r a1b640641d37 es/examples/tag --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tag Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/template.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.simple Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/template.svnstyle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.svnstyle Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/tour --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour-merge-conflict Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filelog.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/filenames.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filenames.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,341 @@ +\chapter{File names and pattern matching} +\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 archivos} + +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{Case sensitivity} +\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 +%TODO aarrrgh, más case!!! +\emph{robusto frente a case sensitivity/insensitivity}. Los nombres de +fichero son traducidos para que puedan ser almacenados de manera +%TODO GRRRRR +segura tanto en sistemas case sensitive o case insensitive. 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} +%TODO FIXME continuar aca, me niego a seguir traduciendo esto de +%momento + +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 97e929385442 -r a1b640641d37 es/fixhtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixhtml.py Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/fixhtml.py \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/fixsvg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixsvg Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/fixsvg \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgbook.css Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/hgbook.css \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/hgext.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgext.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 archivo 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 archivos 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 archivos. 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 daemoniot 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 +configurarl. 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 97e929385442 -r a1b640641d37 es/hook.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hook.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1452 @@ +\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 +socual 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} hook to control commits} + \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{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 97e929385442 -r a1b640641d37 es/htlatex.book --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/htlatex.book Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,1 @@ +../en/htlatex.book \ No newline at end of file diff -r 97e929385442 -r a1b640641d37 es/intro.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/intro.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,617 @@ +\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, el +siguiente mayor que el anterior. + +Administrar manualmente muchas versiones de 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 decenas 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 Contar con la historia 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 de forma casi simultanea pueden hacer cambios + incompatibles, 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 permitirá trabajar simultáneamente, y manejar las diferencias + entre múltiples versiones de su proyecto. +\end{itemize} +La mayoría de estas razones son igualmente validas ---por lo menos en +teoría--- así esté trabajando en un proyecto solo, o con mucha gente. + +Algo fundamental acerca de lo práctico de un sistema de control de +revisiones en estas dos escalas (``un hacker solo'' 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 de control de +versiones y un proceso. En este caso, el costo de usar control de +revisiones ni siquiera se tiene en cueant, 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 solamente soporta \emph{ambas} escalas de de +desarrollo. 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 +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 puede trabajar en +el proyecto. + +\subsection{La cantidad de nombres del control de revisiones} + +El control de revisiones es un campo amplio, tan ampli 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 sobrelapan tanto que no hay un +acuerdo o una forma 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 gratutita 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 estuvieran modificando +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 impedian 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 todo el fichero del proyecto y podía +modificar su copia 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, haciéndolos públicos en 1989, código sobre el cual se ha +desarrollado la versión moderna de CVS. CVS posteriormente adquirió +la habilidad de operar sobre una conexión de red, dotándolo de una +arquitectura, cliente/servidor. La arquitectura de CVS es +centralizada; La historia 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 revisión de controles llamado TeamWare +Un espacio de trabajo TeamWare contiene una copia completa de la +historia del proyecto. TeamWare no tiene la noción de repositorio +central. (CVS se basaba en RCS para el almacenamiento de su historia; +TeamWare usaba SCCS.) + +A medida que avanzaba la decada de los noventa, se empezño a +evidenciar los problemas de CVS. Alacena 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 bien; es fácil desordenar un repositorio renombrando ficheros +y directorios. Peor aún, su código fuente es difícil de leer y +mantener, lo que hace que su ``umbral de dolor'' para arreglar sus +problemas arquitecturales 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 nombres de espacios, y otras características que lo hacen +mejor que CVS. Desde su versión inicial, ha ido creciendo en +popularidad. + +Más o menos en forma simultánea Graydon Hoare comenzó a trabajar en un +sistema distribuido de control de versiones ambicioso 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á(junto con otros proyectos subsecuentes) que unas herramientas 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 varias tendencias en el desarrollo y uso de las herramientas +de control de revisiones en las pasadas cuatro décadas, mientras la +gente se ha vuelto familiar con las capacidades de sus herramientas +así mismo con 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 limitación a un sólo computador, +determinó 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 +uno 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 impidieron +que usuarios remotos se conectaran al servidor. A medida que los +proyecos 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 de +forma natural peer-to-peer. 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 a la cuestión de elección y consenso. Las +herramientas modernas pueden operar sin conexión indefinidamenta 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 robusto y usable como la +generación previa de su contraparte, personas que usan 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 las herramientas centralizadas No puede +siquiera usar una herramienta centralizada sin conexión de red, +excepto con 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 significante. + +\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 un par con la gente que se +considera el ``alma'' del proyecto. Si ellos publican los +repositorios, se puede copiar inmediatamente la historia del proyecto, +hacer cambios y guardar su trabajo, usando las mismas herramientas de +la misma forma que ellos. En contraste, con una herramienta +centralizada, 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 ``bifurcanción''\ndt{fork.} del +desarrollo del proyecto. Una bifurcación pasa cuando hay diferencias +de opinión o actitud entre grupos de desarrolladores que desenvoca 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. Tiene que decidir qué historia de revisiones va a +``win'', 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 +enmarca 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 las herramientas centralizadas + imponen: esto entre los 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 da tal control. En todo caso, si este +es su parecer, y publica sus repositorios de CVS o Subversion, hay +muchas herramientas disponibles que pueden obtener la historia +completa(A pesar de lo lento) y recrearla 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 historia. + +\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 las órdenes 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 +administradr. 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 quw costosos sistemas centralizados caigan +ante la carga combinada de unas cuantas docenas de usuarios +concurrentes. De nuevo, las respuestas típicas de replibcació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 sólo 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 scripts. + +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 la +historia 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 +particularmente una buena elección como un 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, 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, puede comenzar a trabajar con Mercurial poco a +poco. Creando nuevos cambios y ramas, transfiriendo cambios(localmente +o por la red); y las operaciones relacionadas con el estado y la +historia 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 el mejoramiento de 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. Su arquitectura es centralizada +en cliente/servidor. + +Subversion y Mercurial tienen órdenes 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 el 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 sencillos 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 sencillas, la capacidad +del servidor y el ancho de banda se convierten en cuellos de botella +para proyectos modestamente grandes. + +Adicionalmente, Subversion tiene un sobrecosto en almacentamiento +considerable 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 la +historia completa 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 la historia 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 la historia de revisiones de un repositorio +de Subversion. También puede exportar historia 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 de la historia 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á lejos detrás de +Mercurial. + +Mientras que el repositorio de Mercurial no requiere mantenimiento, el +repositorio de Git requiere frecuentes ``repacks'' a sus metadatos. +Sin estos, el desempeño se degrada y el 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 scripts de shell o Perl y la calidad de esos +scripts varía ampliamente. He encontrado muchas situaciones en las +cuales los scripts 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 +nterna, ha sido ligeramente mantenido en muchos años. + +Tiene una arquitectura centralizada cliente/servidor. No agrupa +cambios relacionadso 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 la historia 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 de buena forma, 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 la historia de revisiones de CVS. De todas +maneras hay ciertos trucos para aplicar; los cuales también son +necesarios 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 archivos, es imposible reconstruir la +completamente la historia de CVS acertadamente; 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 +archivos que han permanecido con candados por más de una década son +dos de los problemas más interesantes de los que puedo retomar de mi +experiencia personal). + +Mercurial puede importar la historia de revisiones de un repositorio +CVS. + +\subsection{Herramientas comerciales} + +Perforce tiene una arquitectura centralizada cliente/servidor sin +almacenamiento de dato alguno en el lado del cliente. A diferencia de +las herramientas modernas de control de revisiones, Perforce requiere +que un usuario ejecute una orden 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 que los hacen valiosos 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 la historia 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 la +orden\hgext{convert} soporta: +\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. + +La orden \hgxcmd{conver}{convert} es sencilla de usar. Basta con +apuntarla 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 la orden para importar nuevos cambios. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 97e929385442 -r a1b640641d37 es/kdiff3.png Binary file es/kdiff3.png has changed diff -r 97e929385442 -r a1b640641d37 es/license.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/license.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/metadata.svg Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitácora de cambios + Manifiesto + Bitácora de archivos + + diff -r 97e929385442 -r a1b640641d37 es/mq-collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-collab.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,393 @@ +\chapter{Advanced uses of Mercurial Queues} +\label{chap:mq-collab} + +While it's easy to pick up straightforward uses of Mercurial Queues, +use of a little discipline and some of MQ's less frequently used +capabilities makes it possible to work in complicated development +environments. + +In this chapter, I will use as an example a technique I have used to +manage the development of an Infiniband device driver for the Linux +kernel. The driver in question is large (at least as drivers go), +with 25,000 lines of code spread across 35 source files. It is +maintained by a small team of developers. + +While much of the material in this chapter is specific to Linux, the +same principles apply to any code base for which you're not the +primary owner, and upon which you need to do a lot of development. + +\section{The problem of many targets} + +The Linux kernel changes rapidly, and has never been internally +stable; developers frequently make drastic changes between releases. +This means that a version of the driver that works well with a +particular released version of the kernel will not even \emph{compile} +correctly against, typically, any other version. + +To maintain a driver, we have to keep a number of distinct versions of +Linux in mind. +\begin{itemize} +\item One target is the main Linux kernel development tree. + Maintenance of the code is in this case partly shared by other + developers in the kernel community, who make ``drive-by'' + modifications to the driver as they develop and refine kernel + subsystems. +\item We also maintain a number of ``backports'' to older versions of + the Linux kernel, to support the needs of customers who are running + older Linux distributions that do not incorporate our drivers. (To + \emph{backport} a piece of code is to modify it to work in an older + version of its target environment than the version it was developed + for.) +\item Finally, we make software releases on a schedule that is + necessarily not aligned with those used by Linux distributors and + kernel developers, so that we can deliver new features to customers + without forcing them to upgrade their entire kernels or + distributions. +\end{itemize} + +\subsection{Tempting approaches that don't work well} + +There are two ``standard'' ways to maintain a piece of software that +has to target many different environments. + +The first is to maintain a number of branches, each intended for a +single target. The trouble with this approach is that you must +maintain iron discipline in the flow of changes between repositories. +A new feature or bug fix must start life in a ``pristine'' repository, +then percolate out to every backport repository. Backport changes are +more limited in the branches they should propagate to; a backport +change that is applied to a branch where it doesn't belong will +probably stop the driver from compiling. + +The second is to maintain a single source tree filled with conditional +statements that turn chunks of code on or off depending on the +intended target. Because these ``ifdefs'' are not allowed in the +Linux kernel tree, a manual or automatic process must be followed to +strip them out and yield a clean tree. A code base maintained in this +fashion rapidly becomes a rat's nest of conditional blocks that are +difficult to understand and maintain. + +Neither of these approaches is well suited to a situation where you +don't ``own'' the canonical copy of a source tree. In the case of a +Linux driver that is distributed with the standard kernel, Linus's +tree contains the copy of the code that will be treated by the world +as canonical. The upstream version of ``my'' driver can be modified +by people I don't know, without me even finding out about it until +after the changes show up in Linus's tree. + +These approaches have the added weakness of making it difficult to +generate well-formed patches to submit upstream. + +In principle, Mercurial Queues seems like a good candidate to manage a +development scenario such as the above. While this is indeed the +case, MQ contains a few added features that make the job more +pleasant. + +\section{Conditionally applying patches with + guards} + +Perhaps the best way to maintain sanity with so many targets is to be +able to choose specific patches to apply for a given situation. MQ +provides a feature called ``guards'' (which originates with quilt's +\texttt{guards} command) that does just this. To start off, let's +create a simple repository for experimenting in. +\interaction{mq.guards.init} +This gives us a tiny repository that contains two patches that don't +have any dependencies on each other, because they touch different files. + +The idea behind conditional application is that you can ``tag'' a +patch with a \emph{guard}, which is simply a text string of your +choosing, then tell MQ to select specific guards to use when applying +patches. MQ will then either apply, or skip over, a guarded patch, +depending on the guards that you have selected. + +A patch can have an arbitrary number of guards; +each one is \emph{positive} (``apply this patch if this guard is +selected'') or \emph{negative} (``skip this patch if this guard is +selected''). A patch with no guards is always applied. + +\section{Controlling the guards on a patch} + +The \hgxcmd{mq}{qguard} command lets you determine which guards should +apply to a patch, or display the guards that are already in effect. +Without any arguments, it displays the guards on the current topmost +patch. +\interaction{mq.guards.qguard} +To set a positive guard on a patch, prefix the name of the guard with +a ``\texttt{+}''. +\interaction{mq.guards.qguard.pos} +To set a negative guard on a patch, prefix the name of the guard with +a ``\texttt{-}''. +\interaction{mq.guards.qguard.neg} + +\begin{note} + The \hgxcmd{mq}{qguard} command \emph{sets} the guards on a patch; it + doesn't \emph{modify} them. What this means is that if you run + \hgcmdargs{qguard}{+a +b} on a patch, then \hgcmdargs{qguard}{+c} on + the same patch, the \emph{only} guard that will be set on it + afterwards is \texttt{+c}. +\end{note} + +Mercurial stores guards in the \sfilename{series} file; the form in +which they are stored is easy both to understand and to edit by hand. +(In other words, you don't have to use the \hgxcmd{mq}{qguard} command if +you don't want to; it's okay to simply edit the \sfilename{series} +file.) +\interaction{mq.guards.series} + +\section{Selecting the guards to use} + +The \hgxcmd{mq}{qselect} command determines which guards are active at a +given time. The effect of this is to determine which patches MQ will +apply the next time you run \hgxcmd{mq}{qpush}. It has no other effect; in +particular, it doesn't do anything to patches that are already +applied. + +With no arguments, the \hgxcmd{mq}{qselect} command lists the guards +currently in effect, one per line of output. Each argument is treated +as the name of a guard to apply. +\interaction{mq.guards.qselect.foo} +In case you're interested, the currently selected guards are stored in +the \sfilename{guards} file. +\interaction{mq.guards.qselect.cat} +We can see the effect the selected guards have when we run +\hgxcmd{mq}{qpush}. +\interaction{mq.guards.qselect.qpush} + +A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}'' +character. The name of a guard must not contain white space, but most +other characters are acceptable. If you try to use a guard with an +invalid name, MQ will complain: +\interaction{mq.guards.qselect.error} +Changing the selected guards changes the patches that are applied. +\interaction{mq.guards.qselect.quux} +You can see in the example below that negative guards take precedence +over positive guards. +\interaction{mq.guards.qselect.foobar} + +\section{MQ's rules for applying patches} + +The rules that MQ uses when deciding whether to apply a patch +are as follows. +\begin{itemize} +\item A patch that has no guards is always applied. +\item If the patch has any negative guard that matches any currently + selected guard, the patch is skipped. +\item If the patch has any positive guard that matches any currently + selected guard, the patch is applied. +\item If the patch has positive or negative guards, but none matches + any currently selected guard, the patch is skipped. +\end{itemize} + +\section{Trimming the work environment} + +In working on the device driver I mentioned earlier, I don't apply the +patches to a normal Linux kernel tree. Instead, I use a repository +that contains only a snapshot of the source files and headers that are +relevant to Infiniband development. This repository is~1\% the size +of a kernel repository, so it's easier to work with. + +I then choose a ``base'' version on top of which the patches are +applied. This is a snapshot of the Linux kernel tree as of a revision +of my choosing. When I take the snapshot, I record the changeset ID +from the kernel repository in the commit message. Since the snapshot +preserves the ``shape'' and content of the relevant parts of the +kernel tree, I can apply my patches on top of either my tiny +repository or a normal kernel tree. + +Normally, the base tree atop which the patches apply should be a +snapshot of a very recent upstream tree. This best facilitates the +development of patches that can easily be submitted upstream with few +or no modifications. + +\section{Dividing up the \sfilename{series} file} + +I categorise the patches in the \sfilename{series} file into a number +of logical groups. Each section of like patches begins with a block +of comments that describes the purpose of the patches that follow. + +The sequence of patch groups that I maintain follows. The ordering of +these groups is important; I'll describe why after I introduce the +groups. +\begin{itemize} +\item The ``accepted'' group. Patches that the development team has + submitted to the maintainer of the Infiniband subsystem, and which + he has accepted, but which are not present in the snapshot that the + tiny repository is based on. These are ``read only'' patches, + present only to transform the tree into a similar state as it is in + the upstream maintainer's repository. +\item The ``rework'' group. Patches that I have submitted, but that + the upstream maintainer has requested modifications to before he + will accept them. +\item The ``pending'' group. Patches that I have not yet submitted to + the upstream maintainer, but which we have finished working on. + These will be ``read only'' for a while. If the upstream maintainer + accepts them upon submission, I'll move them to the end of the + ``accepted'' group. If he requests that I modify any, I'll move + them to the beginning of the ``rework'' group. +\item The ``in progress'' group. Patches that are actively being + developed, and should not be submitted anywhere yet. +\item The ``backport'' group. Patches that adapt the source tree to + older versions of the kernel tree. +\item The ``do not ship'' group. Patches that for some reason should + never be submitted upstream. For example, one such patch might + change embedded driver identification strings to make it easier to + distinguish, in the field, between an out-of-tree version of the + driver and a version shipped by a distribution vendor. +\end{itemize} + +Now to return to the reasons for ordering groups of patches in this +way. We would like the lowest patches in the stack to be as stable as +possible, so that we will not need to rework higher patches due to +changes in context. Putting patches that will never be changed first +in the \sfilename{series} file serves this purpose. + +We would also like the patches that we know we'll need to modify to be +applied on top of a source tree that resembles the upstream tree as +closely as possible. This is why we keep accepted patches around for +a while. + +The ``backport'' and ``do not ship'' patches float at the end of the +\sfilename{series} file. The backport patches must be applied on top +of all other patches, and the ``do not ship'' patches might as well +stay out of harm's way. + +\section{Maintaining the patch series} + +In my work, I use a number of guards to control which patches are to +be applied. + +\begin{itemize} +\item ``Accepted'' patches are guarded with \texttt{accepted}. I + enable this guard most of the time. When I'm applying the patches + on top of a tree where the patches are already present, I can turn + this patch off, and the patches that follow it will apply cleanly. +\item Patches that are ``finished'', but not yet submitted, have no + guards. If I'm applying the patch stack to a copy of the upstream + tree, I don't need to enable any guards in order to get a reasonably + safe source tree. +\item Those patches that need reworking before being resubmitted are + guarded with \texttt{rework}. +\item For those patches that are still under development, I use + \texttt{devel}. +\item A backport patch may have several guards, one for each version + of the kernel to which it applies. For example, a patch that + backports a piece of code to~2.6.9 will have a~\texttt{2.6.9} guard. +\end{itemize} +This variety of guards gives me considerable flexibility in +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 97e929385442 -r a1b640641d37 es/mq-ref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-ref.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,349 @@ +\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 97e929385442 -r a1b640641d37 es/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-stack.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/mq.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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( aveces 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 de la historia 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 +los tags normalmente en el repositorio con un distintivo para cada +parche aplicado. Adicionalmente, los tags 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 +de el 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 +sobrelaparse, 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 97e929385442 -r a1b640641d37 es/note.png Binary file es/note.png has changed diff -r 97e929385442 -r a1b640641d37 es/preface.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/preface.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/revlog.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/snapshot.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/srcinstall.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/srcinstall.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,53 @@ +\chapter{Installing Mercurial from source} +\label{chap:srcinstall} + +\section{On a Unix-like system} +\label{sec:srcinstall:unixlike} + +If you are using a Unix-like system that has a sufficiently recent +version of Python (2.3~or newer) available, it is easy to install +Mercurial from source. +\begin{enumerate} +\item Download a recent source tarball from + \url{http://www.selenic.com/mercurial/download}. +\item Unpack the tarball: + \begin{codesample4} + gzip -dc mercurial-\emph{version}.tar.gz | tar xf - + \end{codesample4} +\item Go into the source directory and run the installer script. This + will build Mercurial and install it in your home directory. + \begin{codesample4} + cd mercurial-\emph{version} + python setup.py install --force --home=\$HOME + \end{codesample4} +\end{enumerate} +Once the install finishes, Mercurial will be in the \texttt{bin} +subdirectory of your home directory. Don't forget to make sure that +this directory is present in your shell's search path. + +You will probably need to set the \envar{PYTHONPATH} environment +variable so that the Mercurial executable can find the rest of the +Mercurial packages. For example, on my laptop, I have set it to +\texttt{/home/bos/lib/python}. The exact path that you will need to +use depends on how Python was built for your system, but should be +easy to figure out. If you're uncertain, look through the output of +the installer script above, and see where the contents of the +\texttt{mercurial} directory were installed to. + +\section{On Windows} + +Building and installing Mercurial on Windows requires a variety of +tools, a fair amount of technical knowledge, and considerable +patience. I very much \emph{do not recommend} this route if you are a +``casual user''. Unless you intend to hack on Mercurial, I strongly +suggest that you use a binary package instead. + +If you are intent on building Mercurial from source on Windows, follow +the ``hard way'' directions on the Mercurial wiki at +\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall}, +and expect the process to involve a lot of fiddly work. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 97e929385442 -r a1b640641d37 es/template.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/template.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,475 @@ +\chapter{Customising the output of Mercurial} +\label{chap:template} + +Mercurial provides a powerful mechanism to let you control how it +displays information. The mechanism is based on templates. You can +use templates to generate specific output for a single command, or to +customise the entire appearance of the built-in web interface. + +\section{Using precanned output styles} +\label{sec:style} + +Packaged with Mercurial are some output styles that you can use +immediately. A style is simply a precanned template that someone +wrote and installed somewhere that Mercurial can find. + +Before we take a look at Mercurial's bundled styles, let's review its +normal output. + +\interaction{template.simple.normal} + +This is somewhat informative, but it takes up a lot of space---five +lines of output per changeset. The \texttt{compact} style reduces +this to three lines, presented in a sparse manner. + +\interaction{template.simple.compact} + +The \texttt{changelog} style hints at the expressive power of +Mercurial's templating engine. This style attempts to follow the GNU +Project's changelog guidelines\cite{web:changelog}. + +\interaction{template.simple.changelog} + +You will not be shocked to learn that Mercurial's default output style +is named \texttt{default}. + +\subsection{Setting a default style} + +You can modify the output style that Mercurial will use for every +command by editing your \hgrc\ file, naming the style you would +prefer to use. + +\begin{codesample2} + [ui] + style = compact +\end{codesample2} + +If you write a style of your own, you can use it by either providing +the path to your style file, or copying your style file into a +location where Mercurial can find it (typically the \texttt{templates} +subdirectory of your Mercurial install directory). + +\section{Commands that support styles and templates} + +All of Mercurial's ``\texttt{log}-like'' commands let you use styles +and templates: \hgcmd{incoming}, \hgcmd{log}, \hgcmd{outgoing}, and +\hgcmd{tip}. + +As I write this manual, these are so far the only commands that +support styles and templates. Since these are the most important +commands that need customisable output, there has been little pressure +from the Mercurial user community to add style and template support to +other commands. + +\section{The basics of templating} + +At its simplest, a Mercurial template is a piece of text. Some of the +text never changes, while other parts are \emph{expanded}, or replaced +with new text, when necessary. + +Before we continue, let's look again at a simple example of +Mercurial's normal output. + +\interaction{template.simple.normal} + +Now, let's run the same command, but using a template to change its +output. + +\interaction{template.simple.simplest} + +The example above illustrates the simplest possible template; it's +just a piece of static text, printed once for each changeset. The +\hgopt{log}{--template} option to the \hgcmd{log} command tells +Mercurial to use the given text as the template when printing each +changeset. + +Notice that the template string above ends with the text +``\Verb+\n+''. This is an \emph{escape sequence}, telling Mercurial +to print a newline at the end of each template item. If you omit this +newline, Mercurial will run each piece of output together. See +section~\ref{sec:template:escape} for more details of escape sequences. + +A template that prints a fixed string of text all the time isn't very +useful; let's try something a bit more complex. + +\interaction{template.simple.simplesub} + +As you can see, the string ``\Verb+{desc}+'' in the template has been +replaced in the output with the description of each changeset. Every +time Mercurial finds text enclosed in curly braces (``\texttt{\{}'' +and ``\texttt{\}}''), it will try to replace the braces and text with +the expansion of whatever is inside. To print a literal curly brace, +you must escape it, as described in section~\ref{sec:template:escape}. + +\section{Common template keywords} +\label{sec:template:keyword} + +You can start writing simple templates immediately using the keywords +below. + +\begin{itemize} +\item[\tplkword{author}] String. The unmodified author of the changeset. +\item[\tplkword{branches}] String. The name of the branch on which + the changeset was committed. Will be empty if the branch name was + \texttt{default}. +\item[\tplkword{date}] Date information. The date when the changeset + was committed. This is \emph{not} human-readable; you must pass it + through a filter that will render it appropriately. See + section~\ref{sec:template:filter} for more information on filters. + The date is expressed as a pair of numbers. The first number is a + Unix UTC timestamp (seconds since January 1, 1970); the second is + the offset of the committer's timezone from UTC, in seconds. +\item[\tplkword{desc}] String. The text of the changeset description. +\item[\tplkword{files}] List of strings. All files modified, added, or + removed by this changeset. +\item[\tplkword{file\_adds}] List of strings. Files added by this + changeset. +\item[\tplkword{file\_dels}] List of strings. Files removed by this + changeset. +\item[\tplkword{node}] String. The changeset identification hash, as a + 40-character hexadecimal string. +\item[\tplkword{parents}] List of strings. The parents of the + changeset. +\item[\tplkword{rev}] Integer. The repository-local changeset revision + number. +\item[\tplkword{tags}] List of strings. Any tags associated with the + changeset. +\end{itemize} + +A few simple experiments will show us what to expect when we use these +keywords; you can see the results in +figure~\ref{fig:template:keywords}. + +\begin{figure} + \interaction{template.simple.keywords} + \caption{Template keywords in use} + \label{fig:template:keywords} +\end{figure} + +As we noted above, the date keyword does not produce human-readable +output, so we must treat it specially. This involves using a +\emph{filter}, about which more in section~\ref{sec:template:filter}. + +\interaction{template.simple.datekeyword} + +\section{Escape sequences} +\label{sec:template:escape} + +Mercurial's templating engine recognises the most commonly used escape +sequences in strings. When it sees a backslash (``\Verb+\+'') +character, it looks at the following character and substitutes the two +characters with a single replacement, as described below. + +\begin{itemize} +\item[\Verb+\textbackslash\textbackslash+] Backslash, ``\Verb+\+'', + ASCII~134. +\item[\Verb+\textbackslash n+] Newline, ASCII~12. +\item[\Verb+\textbackslash r+] Carriage return, ASCII~15. +\item[\Verb+\textbackslash t+] Tab, ASCII~11. +\item[\Verb+\textbackslash v+] Vertical tab, ASCII~13. +\item[\Verb+\textbackslash \{+] Open curly brace, ``\Verb+{+'', ASCII~173. +\item[\Verb+\textbackslash \}+] Close curly brace, ``\Verb+}+'', ASCII~175. +\end{itemize} + +As indicated above, if you want the expansion of a template to contain +a literal ``\Verb+\+'', ``\Verb+{+'', or ``\Verb+{+'' character, you +must escape it. + +\section{Filtering keywords to change their results} +\label{sec:template:filter} + +Some of the results of template expansion are not immediately easy to +use. Mercurial lets you specify an optional chain of \emph{filters} +to modify the result of expanding a keyword. You have already seen a +common filter, \tplkwfilt{date}{isodate}, in action above, to make a +date readable. + +Below is a list of the most commonly used filters that Mercurial +supports. While some filters can be applied to any text, others can +only be used in specific circumstances. The name of each filter is +followed first by an indication of where it can be used, then a +description of its effect. + +\begin{itemize} +\item[\tplfilter{addbreaks}] Any text. Add an XHTML ``\Verb+
+'' + tag before the end of every line except the last. For example, + ``\Verb+foo\nbar+'' becomes ``\Verb+foo
\nbar+''. +\item[\tplkwfilt{date}{age}] \tplkword{date} keyword. Render the + age of the date, relative to the current time. Yields a string like + ``\Verb+10 minutes+''. +\item[\tplfilter{basename}] Any text, but most useful for the + \tplkword{files} keyword and its relatives. Treat the text as a + path, and return the basename. For example, ``\Verb+foo/bar/baz+'' + becomes ``\Verb+baz+''. +\item[\tplkwfilt{date}{date}] \tplkword{date} keyword. Render a date + in a similar format to the Unix \tplkword{date} command, but with + timezone included. Yields a string like + ``\Verb+Mon Sep 04 15:13:13 2006 -0700+''. +\item[\tplkwfilt{author}{domain}] Any text, but most useful for the + \tplkword{author} keyword. Finds the first string that looks like + an email address, and extract just the domain component. For + example, ``\Verb+Bryan O'Sullivan +'' becomes + ``\Verb+serpentine.com+''. +\item[\tplkwfilt{author}{email}] Any text, but most useful for the + \tplkword{author} keyword. Extract the first string that looks like + an email address. For example, + ``\Verb+Bryan O'Sullivan +'' becomes + ``\Verb+bos@serpentine.com+''. +\item[\tplfilter{escape}] Any text. Replace the special XML/XHTML + characters ``\Verb+&+'', ``\Verb+<+'' and ``\Verb+>+'' with + XML entities. +\item[\tplfilter{fill68}] Any text. Wrap the text to fit in 68 + columns. This is useful before you pass text through the + \tplfilter{tabindent} filter, and still want it to fit in an + 80-column fixed-font window. +\item[\tplfilter{fill76}] Any text. Wrap the text to fit in 76 + columns. +\item[\tplfilter{firstline}] Any text. Yield the first line of text, + without any trailing newlines. +\item[\tplkwfilt{date}{hgdate}] \tplkword{date} keyword. Render the + date as a pair of readable numbers. Yields a string like + ``\Verb+1157407993 25200+''. +\item[\tplkwfilt{date}{isodate}] \tplkword{date} keyword. Render the + date as a text string in ISO~8601 format. Yields a string like + ``\Verb+2006-09-04 15:13:13 -0700+''. +\item[\tplfilter{obfuscate}] Any text, but most useful for the + \tplkword{author} keyword. Yield the input text rendered as a + sequence of XML entities. This helps to defeat some particularly + stupid screen-scraping email harvesting spambots. +\item[\tplkwfilt{author}{person}] Any text, but most useful for the + \tplkword{author} keyword. Yield the text before an email address. + For example, ``\Verb+Bryan O'Sullivan +'' + becomes ``\Verb+Bryan O'Sullivan+''. +\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} keyword. Render a + date using the same format used in email headers. Yields a string + like ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+''. +\item[\tplkwfilt{node}{short}] Changeset hash. Yield the short form + of a changeset hash, i.e.~a 12-byte hexadecimal string. +\item[\tplkwfilt{date}{shortdate}] \tplkword{date} keyword. Render + the year, month, and day of the date. Yields a string like + ``\Verb+2006-09-04+''. +\item[\tplfilter{strip}] Any text. Strip all leading and trailing + whitespace from the string. +\item[\tplfilter{tabindent}] Any text. Yield the text, with every line + except the first starting with a tab character. +\item[\tplfilter{urlescape}] Any text. Escape all characters that are + considered ``special'' by URL parsers. For example, \Verb+foo bar+ + becomes \Verb+foo%20bar+. +\item[\tplkwfilt{author}{user}] Any text, but most useful for the + \tplkword{author} keyword. Return the ``user'' portion of an email + address. For example, + ``\Verb+Bryan O'Sullivan +'' becomes + ``\Verb+bos+''. +\end{itemize} + +\begin{figure} + \interaction{template.simple.manyfilters} + \caption{Template filters in action} + \label{fig:template:filters} +\end{figure} + +\begin{note} + If you try to apply a filter to a piece of data that it cannot + process, Mercurial will fail and print a Python exception. For + example, trying to run the output of the \tplkword{desc} keyword + into the \tplkwfilt{date}{isodate} filter is not a good idea. +\end{note} + +\subsection{Combining filters} + +It is easy to combine filters to yield output in the form you would +like. The following chain of filters tidies up a description, then +makes sure that it fits cleanly into 68 columns, then indents it by a +further 8~characters (at least on Unix-like systems, where a tab is +conventionally 8~characters wide). + +\interaction{template.simple.combine} + +Note the use of ``\Verb+\t+'' (a tab character) in the template to +force the first line to be indented; this is necessary since +\tplkword{tabindent} indents all lines \emph{except} the first. + +Keep in mind that the order of filters in a chain is significant. The +first filter is applied to the result of the keyword; the second to +the result of the first filter; and so on. For example, using +\Verb+fill68|tabindent+ gives very different results from +\Verb+tabindent|fill68+. + + +\section{From templates to styles} + +A command line template provides a quick and simple way to format some +output. Templates can become verbose, though, and it's useful to be +able to give a template a name. A style file is a template with a +name, stored in a file. + +More than that, using a style file unlocks the power of Mercurial's +templating engine in ways that are not possible using the command line +\hgopt{log}{--template} option. + +\subsection{The simplest of style files} + +Our simple style file contains just one line: + +\interaction{template.simple.rev} + +This tells Mercurial, ``if you're printing a changeset, use the text +on the right as the template''. + +\subsection{Style file syntax} + +The syntax rules for a style file are simple. + +\begin{itemize} +\item The file is processed one line at a time. + +\item Leading and trailing white space are ignored. + +\item Empty lines are skipped. + +\item If a line starts with either of the characters ``\texttt{\#}'' or + ``\texttt{;}'', the entire line is treated as a comment, and skipped + as if empty. + +\item A line starts with a keyword. This must start with an + alphabetic character or underscore, and can subsequently contain any + alphanumeric character or underscore. (In regexp notation, a + keyword must match \Verb+[A-Za-z_][A-Za-z0-9_]*+.) + +\item The next element must be an ``\texttt{=}'' character, which can + be preceded or followed by an arbitrary amount of white space. + +\item If the rest of the line starts and ends with matching quote + characters (either single or double quote), it is treated as a + template body. + +\item If the rest of the line \emph{does not} start with a quote + character, it is treated as the name of a file; the contents of this + file will be read and used as a template body. +\end{itemize} + +\section{Style files by example} + +To illustrate how to write a style file, we will construct a few by +example. Rather than provide a complete style file and walk through +it, we'll mirror the usual process of developing a style file by +starting with something very simple, and walking through a series of +successively more complete examples. + +\subsection{Identifying mistakes in style files} + +If Mercurial encounters a problem in a style file you are working on, +it prints a terse error message that, once you figure out what it +means, is actually quite useful. + +\interaction{template.svnstyle.syntax.input} + +Notice that \filename{broken.style} attempts to define a +\texttt{changeset} keyword, but forgets to give any content for it. +When instructed to use this style file, Mercurial promptly complains. + +\interaction{template.svnstyle.syntax.error} + +This error message looks intimidating, but it is not too hard to +follow. + +\begin{itemize} +\item The first component is simply Mercurial's way of saying ``I am + giving up''. + \begin{codesample4} + \textbf{abort:} broken.style:1: parse error + \end{codesample4} + +\item Next comes the name of the style file that contains the error. + \begin{codesample4} + abort: \textbf{broken.style}:1: parse error + \end{codesample4} + +\item Following the file name is the line number where the error was + encountered. + \begin{codesample4} + abort: broken.style:\textbf{1}: parse error + \end{codesample4} + +\item Finally, a description of what went wrong. + \begin{codesample4} + abort: broken.style:1: \textbf{parse error} + \end{codesample4} + The description of the problem is not always clear (as in this + case), but even when it is cryptic, it is almost always trivial to + visually inspect the offending line in the style file and see what + is wrong. +\end{itemize} + +\subsection{Uniquely identifying a repository} + +If you would like to be able to identify a Mercurial repository +``fairly uniquely'' using a short string as an identifier, you can +use the first revision in the repository. +\interaction{template.svnstyle.id} +This is not guaranteed to be unique, but it is nevertheless useful in +many cases. +\begin{itemize} +\item It will not work in a completely empty repository, because such + a repository does not have a revision~zero. +\item Neither will it work in the (extremely rare) case where a + repository is a merge of two or more formerly independent + repositories, and you still have those repositories around. +\end{itemize} +Here are some uses to which you could put this identifier: +\begin{itemize} +\item As a key into a table for a database that manages repositories + on a server. +\item As half of a \{\emph{repository~ID}, \emph{revision~ID}\} tuple. + Save this information away when you run an automated build or other + activity, so that you can ``replay'' the build later if necessary. +\end{itemize} + +\subsection{Mimicking Subversion's output} + +Let's try to emulate the default output format used by another +revision control tool, Subversion. +\interaction{template.svnstyle.short} + +Since Subversion's output style is fairly simple, it is easy to +copy-and-paste a hunk of its output into a file, and replace the text +produced above by Subversion with the template values we'd like to see +expanded. +\interaction{template.svnstyle.template} + +There are a few small ways in which this template deviates from the +output produced by Subversion. +\begin{itemize} +\item Subversion prints a ``readable'' date (the ``\texttt{Wed, 27 Sep + 2006}'' in the example output above) in parentheses. Mercurial's + templating engine does not provide a way to display a date in this + format without also printing the time and time zone. +\item We emulate Subversion's printing of ``separator'' lines full of + ``\texttt{-}'' characters by ending the template with such a line. + We use the templating engine's \tplkword{header} keyword to print a + separator line as the first line of output (see below), thus + achieving similar output to Subversion. +\item Subversion's output includes a count in the header of the number + of lines in the commit message. We cannot replicate this in + Mercurial; the templating engine does not currently provide a filter + that counts the number of items it is passed. +\end{itemize} +It took me no more than a minute or two of work to replace literal +text from an example of Subversion's output with some keywords and +filters to give the template above. The style file simply refers to +the template. +\interaction{template.svnstyle.style} + +We could have included the text of the template file directly in the +style file by enclosing it in quotes and replacing the newlines with +``\verb!\n!'' sequences, but it would have made the style file too +difficult to read. Readability is a good guide when you're trying to +decide whether some text belongs in a style file, or in a template +file that the style file points to. If the style file will look too +big or cluttered if you insert a literal piece of text, drop it into a +template instead. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 97e929385442 -r a1b640641d37 es/tour-basic.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-basic.tex Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,691 @@ +\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 de el 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 la historia +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} +% TODO unificar con Igor, si historia o historial +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{conjuntos 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 + changeset). + +\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 de el 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 de el +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 de el 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 de el 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} +de el 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 97e929385442 -r a1b640641d37 es/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-history.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-conflict.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-merge.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-pull.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-sep-repos.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/tour-merge.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 de el 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 archivo 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 archivos 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 97e929385442 -r a1b640641d37 es/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual-merge.dot Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual.dot Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-non-tip.dot Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-simple.dot Sat Dec 27 09:30:45 2008 -0500 @@ -0,0 +1,4 @@ +digraph undo_simple { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "reversar\nsegundo cambio"; +} diff -r 97e929385442 -r a1b640641d37 es/undo.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo.tex Sat Dec 27 09:30:45 2008 -0500 @@ -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 la historia 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 la historia 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 la historia, +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} a la historia, 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 la historia 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 de la historia de cambios. Como puede ver, la historia es +bonita 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 la historia gráfica 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 historia 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 de la +historia 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 de la historia +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, la historia 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 historia 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 la historia---cada +cambio se coloca encima de todos los cambios que leo +preceden---usualmente usted no puede hacer que unos cambios desastros +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 la historia, 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'' de la +historia 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'' +de la historia 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 historia 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 historia, 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 97e929385442 -r a1b640641d37 es/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-after-commit.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-branch.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-merge.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-pre-branch.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 97e929385442 -r a1b640641d37 es/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir.svg Sat Dec 27 09:30:45 2008 -0500 @@ -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 + + + +